How can I implement a click to select using ViewModel

I have this XAML that displays 6 TextCells that can show a check mark or not. They are also activated or not included:

<TableSection Title="Front Side" x:Name="cfsSection">
   <local:CustomTextCell Text="{Binding [0].Name}" IsChecked="{Binding [0].IsSelected}" IsEnabled="{Binding [0].IsSelected, Converter={StaticResource InverseBoolConverter} } "Tapped="cfsSelectValue"/>
   <local:CustomTextCell Text="{Binding [1].Name}" IsChecked="{Binding [1].IsSelected}" IsEnabled="{Binding [1].IsSelected, Converter={StaticResource InverseBoolConverter} } "Tapped="cfsSelectValue"/>
   <local:CustomTextCell Text="{Binding [2].Name}" IsChecked="{Binding [2].IsSelected}" IsEnabled="{Binding [2].IsSelected, Converter={StaticResource InverseBoolConverter} } "Tapped="cfsSelectValue"/>
   <local:CustomTextCell Text="{Binding [3].Name}" IsChecked="{Binding [3].IsSelected}" IsEnabled="{Binding [3].IsSelected, Converter={StaticResource InverseBoolConverter} } "Tapped="cfsSelectValue"/>
   <local:CustomTextCell Text="{Binding [4].Name}" IsChecked="{Binding [4].IsSelected}" IsEnabled="{Binding [4].IsSelected, Converter={StaticResource InverseBoolConverter} } "Tapped="cfsSelectValue"/>
   <local:CustomTextCell Text="{Binding [5].Name}" IsChecked="{Binding [5].IsSelected}" IsEnabled="{Binding [5].IsSelected, Converter={StaticResource InverseBoolConverter} } "Tapped="cfsSelectValue"/>
</TableSection>

The code behind is pretty simple, I think. It declares an SSVViewModel array, and the binding causes the text to display:

    SSVViewModel[] CFS = new[] {
        new SSVViewModel {Id = 0, Name=LANG.English.Text(), IsSelected = false},
        new SSVViewModel {Id = 1, Name=LANG.Romaji.Text(), IsSelected = false},
        new SSVViewModel {Id = 2, Name=LANG.Kana.Text(), IsSelected = false},
        new SSVViewModel {Id = 3, Name=LANG.Kanji.Text(), IsSelected = false},
        new SSVViewModel {Id = 4, Name=LANG.KanjiKana.Text(), IsSelected = false},
        new SSVViewModel {Id = 5, Name=LANG.KanaKanji.Text(), IsSelected = false},
    };

When you click on a cell, this function is called:

void cfsSelectValue(object sender, EventArgs e) 
{
        var cell = sender as TextCell;
        if (cell == null)
            return;

        var selected = cell.Text;

        foreach (var setting in CFS)
            setting.IsSelected = false;

        foreach (var setting in CFS)
            if (setting.Name == selected)
                setting.IsSelected = true;

 }

However, when you click on one of the first two cells, they are displayed as marked. All other clicks on the cells work fine. In another part of my code, I use a similar construct, and these are the last two cells that do not work.

Note that IsEnabled works, but not IsChecked

- , - . , , . , , IsSelected false, , , .

, : setting.IsSelected = false; : setting.IsSelected = true; , , IsSelected true, - false. , , , .

viewModel, :

public class SSVViewModel: ObservableProperty
{
    private int id;
    private string name;
    private bool isSelected;

    public int Id
    {
        get
        {
            return id;
        }
        set
        {
            if (value != id)
            {
                id = value;
                NotifyPropertyChanged("Id");
            }
        }
    }

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != name)
            {
                name = value;
                NotifyPropertyChanged("Name");
            }
        }
    }

    public bool IsSelected
    {
        get
        {
            return isSelected;
        }
        set
        {
            if (value != isSelected)
            {
                isSelected = value;
                NotifyPropertyChanged("IsSelected");
            }
        }
    }
}

CustomTextCellRenderer

public class CustomTextCellRenderer : TextCellRenderer
{
    UITableViewCell _nativeCell;

    public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
    {
        _nativeCell = base.GetCell(item, reusableCell, tv);
        var formsCell = item as CustomTextCell;

        if (formsCell != null)
        {
            formsCell.PropertyChanged -= OnPropertyChanged;
            formsCell.PropertyChanged += OnPropertyChanged;
        }

        SetCheckmark(formsCell);
        SetTap(formsCell);

        return _nativeCell;
    }

    void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        var formsCell = sender as CustomTextCell;
        if (formsCell == null)
            return;

        if (e.PropertyName == CustomTextCell.IsCheckedProperty.PropertyName)
        {
            SetCheckmark(formsCell);
        }

        if (e.PropertyName == CustomTextCell.NoTapProperty.PropertyName)
        {
            SetTap(formsCell);
        }
    }

    private void SetCheckmark(CustomTextCell formsCell)
    {
        if (formsCell.IsChecked)
            _nativeCell.Accessory = UITableViewCellAccessory.Checkmark;
        else
            _nativeCell.Accessory = UITableViewCellAccessory.None;
    }

    private void SetTap(CustomTextCell formsCell)
    {
        if (formsCell.NoTap)
            _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
        else
            _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.Default;
    }

}

1

<local:CustomTextCell           Text="{Binding [0].Name}" IsChecked="{Binding [0].IsSelected}" Tapped="cfsSelectValue" CommandParameter="0" />
<local:CustomTextCell           Text="{Binding [1].Name}" IsChecked="{Binding [1].IsSelected}" Tapped="cfsSelectValue" CommandParameter="1" />
<local:CustomTextCell           Text="{Binding [2].Name}" IsChecked="{Binding [2].IsSelected}" Tapped="cfsSelectValue" CommandParameter="2" />
<local:CustomTextCell           Text="{Binding [3].Name}" IsChecked="{Binding [3].IsSelected}" Tapped="cfsSelectValue" CommandParameter="3" />
<local:CustomTextCell           Text="{Binding [4].Name}" IsChecked="{Binding [4].IsSelected}" Tapped="cfsSelectValue" CommandParameter="4" />

, , , . :

iOS:

    private void SetCheckmark(CustomTextCell formsCell)
    {
        if (formsCell.IsChecked)
        {
            _nativeCell.Accessory = UITableViewCellAccessory.Checkmark;
            Debug.WriteLine(_nativeCell.TextLabel.Text + " checked");
        }
        else
        {
            _nativeCell.Accessory = UITableViewCellAccessory.None;
            Debug.WriteLine(_nativeCell.TextLabel.Text + " unchecked");
        }
    }

, JLPT N2:

Category Group unchecked
Category unchecked
All Available Words unchecked
Japanese for Busy People 1 unchecked
Japanese for Busy People 2 unchecked
Japanese for Busy People 3 unchecked
JLPT Level N5 unchecked
JLPT Level N4 unchecked
JLPT Level N3 unchecked
JLPT Level N2 unchecked
JLPT Level N1 checked
JLPT Level N2 checked
JLPT Level N3 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N2 checked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked
JLPT Level N1 unchecked

, .

, , , N2 , N3 N2 .

, , , iOS , . , iOS. -. , , , cell = tv.DequeueReusableCell(fullName) as CellTableViewCell;

public class TextCellCustomRenderer : TextCellRenderer
{
    CellTableViewCell cell;
    public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
    {
        var textCell = (TextCell)item;
        var fullName = item.GetType().FullName;
        cell = tv.DequeueReusableCell(fullName) as CellTableViewCell;

        if (cell == null)
        {
            cell = new CellTableViewCell(UITableViewCellStyle.Value1, fullName);
        }
        else
        {
            cell.Cell.PropertyChanged -= cell.HandlePropertyChanged;
            //cell.Cell.PropertyChanged -= Current_PropertyChanged;
        }

        cell.Cell = textCell;
        textCell.PropertyChanged += cell.HandlePropertyChanged;
        cell.PropertyChanged = this.HandlePropertyChanged;
        cell.SelectionStyle = UITableViewCellSelectionStyle.None;
        cell.TextLabel.Text = textCell.Text;
        cell.DetailTextLabel.Text = textCell.Detail;
        cell.ContentView.BackgroundColor = UIColor.White;


        switch (item.StyleId)
        {
            case "checkmark":
                cell.Accessory = UIKit.UITableViewCellAccessory.Checkmark;
                break;
            case "detail-button":
                cell.Accessory = UIKit.UITableViewCellAccessory.DetailButton;
                break;
            case "detail-disclosure-button":
                cell.Accessory = UIKit.UITableViewCellAccessory.DetailDisclosureButton;
                break;
            case "disclosure":
                cell.Accessory = UIKit.UITableViewCellAccessory.DisclosureIndicator;
                break;
            case "none":
            default:
                cell.Accessory = UIKit.UITableViewCellAccessory.None;
                break;
        }

        //UpdateBackground(cell, item);

        return cell;
    }

    void checkAccessoryVisibility() {

    }

}
+6
3

, , property-changed-handler , .

TextCellRenderer -changed-event, HandlePropertyChanged, .

( ) :

public class CustomTextCellRenderer : TextCellRenderer
{
    public override UITableViewCell GetCell(Cell item, UITableViewCell reusableCell, UITableView tv)
    {
        var nativeCell = base.GetCell(item, reusableCell, tv);

        if (item is CustomTextCell formsCell)
        {
            SetCheckmark(nativeCell, formsCell);
            SetTap(nativeCell, formsCell);
        }

        return nativeCell;
    }

    protected override void HandlePropertyChanged(object sender, PropertyChangedEventArgs args)
    {
        base.HandlePropertyChanged(sender, args);

        System.Diagnostics.Debug.WriteLine($"HandlePropertyChanged {args.PropertyName}");

        var nativeCell = sender as CellTableViewCell;
        if (nativeCell?.Element is CustomTextCell formsCell)
        {
            if (args.PropertyName == CustomTextCell.IsCheckedProperty.PropertyName)
                SetCheckmark(nativeCell, formsCell);
            else if (args.PropertyName == CustomTextCell.NoTapProperty.PropertyName)
                SetTap(nativeCell, formsCell);
        }
    }

    void SetCheckmark(UITableViewCell nativeCell, CustomTextCell formsCell)
    {
        if (formsCell.IsChecked)
            nativeCell.Accessory = UITableViewCellAccessory.Checkmark;
        else
            nativeCell.Accessory = UITableViewCellAccessory.None;
    }

    void SetTap(UITableViewCell nativeCell, CustomTextCell formsCell)
    {
        if (formsCell.NoTap)
            _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.None;
        else
            _nativeCell.SelectionStyle = UITableViewCellSelectionStyle.Default;
    }
}
+5

, , :

, , RadioButton. Xamarin.Forms , , .

cfsSelectValue.

local:CustomTextCells IsSelected false TextCell, , sender.


:

:

ViewModels ObservableCollection, Array

private ObservableCollection<SSVViewModel> viewModels = new ObservableCollection<SSVViewModel>()
{
    new SSVViewModel {Id = 0, Name=LANG.English.Text()},
    new SSVViewModel {Id = 1, Name=LANG.Romaji.Text()},
    new SSVViewModel {Id = 2, Name=LANG.Kana.Text()},
    new SSVViewModel {Id = 3, Name=LANG.Kanji.Text()},
    new SSVViewModel {Id = 4, Name=LANG.KanjiKana.Text()},
    new SSVViewModel {Id = 5, Name=LANG.KanaKanji.Text()},
};

ViewModel INotifyPropertyChanged, ObservableProperty

public class SSVViewModel : INotifyPropertyChanged
{
    private int id;
    private string name;
    private bool isSelected = false; //Set the default here

    public int Id
    {
        get
        {
            return id;
        }
        set
        {
            if (value != id)
            {
                id = value;
                OnPropertyChanged();
            }
        }
    }

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != name)
            {
                name = value;
                OnPropertyChanged();
            }
        }
    }

    public bool IsSelected
    {
        get
        {
            return isSelected;
        }
        set
        {
            if (value != isSelected)
            {
                isSelected = value;
                OnPropertyChanged();
            }
        }
    }


    #region Implementation of INotifyPropertyChanged

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged([CallerMemberName] string propertyname = null)
    {
        if(PropertyChanged != null)
        {
            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyname));
        }
    }

    #endregion
} 

cfsSelectValue :

public void cfsSelectValue(object sender, EventArgs e)
{
    //GetCurrentCell
    CustomTextCell cell = sender as CustomTextCell;
    if (cell == null)
    {
        return;
    }            

    foreach (SSVViewModel viewModel in CFS)
    {
        /*
        Since there is no Tag Property we gotta use something different
        you could use `CommandParameter` since it is of type object

        */
        if (viewModel.Name == cell.Text && viewModel.Id == int.Parse(cell.CommandParameter.ToString()))
        {
            viewModel.IsSelected = true;
        }
        else
        {
            viewModel.IsSelected = false;
        }                    
    }
}
+5

- , , , .

, . i.e XAML :

<TableSection Title="Front Side" x:Name="cfsSection">
   <local:CustomTextCell BindingContext="{Binding [0]}" Text="{Binding Name}" IsChecked="{Binding IsSelected}" Tapped="cfsSelectValue"/>
   <local:CustomTextCell BindingContext="{Binding [1]}" Text="{Binding Name}" IsChecked="{Binding IsSelected}" Tapped="cfsSelectValue"/>
   <local:CustomTextCell BindingContext="{Binding [2]}" Text="{Binding Name}" IsChecked="{Binding IsSelected}" Tapped="cfsSelectValue"/>
   <local:CustomTextCell BindingContext="{Binding [3]}" Text="{Binding Name}" IsChecked="{Binding IsSelected}" Tapped="cfsSelectValue"/>
   <local:CustomTextCell BindingContext="{Binding [4]}" Text="{Binding Name}" IsChecked="{Binding IsSelected}" Tapped="cfsSelectValue"/>
   <local:CustomTextCell BindingContext="{Binding [5]}" Text="{Binding Name}" IsChecked="{Binding IsSelected}" Tapped="cfsSelectValue"/>
</TableSection>

:

void cfsSelectValue(object sender, EventArgs e)
{
    var cell = sender as TextCell;
    if (cell == null)
        return;

    var selected = cell.BindingContext;

    foreach (var setting in CFS)
        setting.IsSelected = (setting == selected);

}
+5
source

All Articles