Boommer
Boommer

Reputation: 103

How to change Gridview Item UI when Observable Collection change object state

I'm trying to only allow one toggle button inside my gridview toggle on. If the next one toggled, the previous toggle button must be de-toggle. the structure of my gridview are these:

  1. GridView bind with an observable collection

  2. Inside a gridview content a user-control represent the gridview item

         <controls:AdaptiveGridView.ItemTemplate>
            <DataTemplate x:DataType="data:FoodDTO">
                <usercontrols:FoodCard FoodId="{x:Bind FoodId}" FoodName="{x:Bind FoodName}" FoodEnglishName="{x:Bind FoodEnglishName}" IsSelected="{Binding IsSelected, Mode=TwoWay}"
                                       MainFoodIcon="{x:Bind MainIcon}" SecondaryFoodIcon="{x:Bind SecondaryIcon}" ToggleClick="FoodCard_ToggleClick"/>
            </DataTemplate>
        </controls:AdaptiveGridView.ItemTemplate>
    
  3. Here is the XAML of my User-Control

    <Grid Height="130" VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Margin="5" Padding="0">
    <ToolkitControls:DropShadowPanel x:Name="DropShadowHolder" VerticalAlignment="Stretch" Margin="10" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
                           BlurRadius="20"
                           ShadowOpacity="0.5" OffsetX="1" OffsetY="20"
                           Color="Black">
        <Grid Background="{ThemeResource SystemAltHighColor}" Height="100" CornerRadius="5" HorizontalAlignment="Stretch"
          VerticalAlignment="Bottom" Padding="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="80"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <ToggleButton Content="CHỌN" FontWeight="Bold" Click="ToggleButton_Click" IsChecked="{Binding IsSelected, Mode=TwoWay}"
                          VerticalAlignment="Bottom" Margin="10,0,0,10" Width="70"  FontSize="12"/>
            <Grid Grid.Column="1" VerticalAlignment="Bottom" Margin="11">
                <StackPanel VerticalAlignment="Bottom">
                    <TextBlock Text="40%" />
                    <ProgressBar Height="30" CornerRadius="3" Value="40"/>
                </StackPanel>
                <PersonPicture Width="25" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,-10,0,0"/>
            </Grid>
            <TextBlock Text="{x:Bind FoodName, Mode=OneWay}" Grid.Column="1" Margin="6,0,0,0"
                   FontSize="15" />
        </Grid>
    </ToolkitControls:DropShadowPanel>
    <StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left">
        <Grid Margin="20,0,0,0">
            <Image x:Name="MainFoodImage" Width="70" Height="70"                       
               VerticalAlignment="Center" HorizontalAlignment="Center"/>
            <Image x:Name="SecondaryFoodImage" Width="20" Height="20"
               VerticalAlignment="Top" HorizontalAlignment="Right"/>
        </Grid>
        <TextBlock Text="{x:Bind FoodEnglishName, Mode=OneWay}" VerticalAlignment="Top" Margin="6,-5,0,0"
                   FontSize="15" FontWeight="Bold" />
    </StackPanel>
    
  4. Here is the code behind of that UserControl

    public sealed partial class FoodCard : UserControl, INotifyPropertyChanged {

    public int FoodId
    {
        get { return (int)GetValue(FoodIdProperty); }
        set { SetValue(FoodIdProperty, value); }
    }
    
    // Using a DependencyProperty as the backing store for FoodId.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FoodIdProperty =
        DependencyProperty.Register("FoodId", typeof(int), typeof(FoodCard), null);
    
    
    
    public string FoodName
    {
        get { return (string)GetValue(FoodNameProperty); }
        set { SetValue(FoodNameProperty, value); }
    }
    
    // Using a DependencyProperty as the backing store for FoodName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FoodNameProperty =
        DependencyProperty.Register("FoodName", typeof(string), typeof(FoodCard), null);
    
    
    
    public string FoodEnglishName
    {
        get { return (string)GetValue(FoodEnglishNameProperty); }
        set { SetValue(FoodEnglishNameProperty, value); }
    }
    
    // Using a DependencyProperty as the backing store for FoodEnglishName.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FoodEnglishNameProperty =
        DependencyProperty.Register("FoodEnglishName", typeof(string), typeof(FoodCard), null);
    
    
    
    public bool IsSelected
    {
        get { return (bool)GetValue(IsSelectedProperty); }
        set { 
            SetValue(IsSelectedProperty, value);
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
            }       
        }
    }
    
    // Using a DependencyProperty as the backing store for IsSelected.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty IsSelectedProperty =
        DependencyProperty.Register("IsSelected", typeof(bool), typeof(FoodCard), new PropertyMetadata(null));
    
    
    public int MainFoodIcon
    {
        get { return (int)GetValue(MainFoodIconProperty); }
        set
        {
              SetValue(MainFoodIconProperty, value);
              MainFoodImage.Source = new BitmapImage(new Uri(_mainFoods[value]));
        }
    }
    
    // Using a DependencyProperty as the backing store for MainFoodIcon.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MainFoodIconProperty =
        DependencyProperty.Register("MainFoodIcon", typeof(int), typeof(FoodCard), null);
    
    
    
    
    
    public int? SecondaryFoodIcon
    {
        get { return (int?)GetValue(SecondaryFoodIconProperty); }
        set
        {
            if(value != null)
            {
                SetValue(SecondaryFoodIconProperty, value);
                SecondaryFoodImage.Source = new BitmapImage(new Uri(_secondaryFoods[value]));
            }
            else SecondaryFoodImage.Source = null;
        }
    }
    
    // Using a DependencyProperty as the backing store for SecondaryFoodIcon.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty SecondaryFoodIconProperty =
        DependencyProperty.Register("SecondaryFoodIcon", typeof(int?), typeof(FoodCard), null);
    
    private readonly IDictionary<int, string> _mainFoods = new Dictionary<int, string>
    {
        { 1, "ms-appx:///Assets/FoodAssets/Rice.png"},
        { 2, "ms-appx:///Assets/FoodAssets/Bread.png"},
        { 3, "ms-appx:///Assets/FoodAssets/Spagheti.png"},
        { 4, "ms-appx:///Assets/FoodAssets/Noodle.png"},
        { 5, "ms-appx:///Assets/FoodAssets/LunchFood.png"}
    };
    
    private readonly IDictionary<int?, string> _secondaryFoods = new Dictionary<int?, string>
    {
        { 6, "ms-appx:///Assets/FoodAssets/Meat.png"},
        { 7, "ms-appx:///Assets/FoodAssets/Chicken.png"},
        { 8, "ms-appx:///Assets/FoodAssets/Egg.png"},
        { 9, "ms-appx:///Assets/FoodAssets/Shrimp.png"},
        { 10, "ms-appx:///Assets/FoodAssets/Falafel.png"}
    };
    
    public event FoodCardEventHandler ToggleClick;
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public FoodCard()
    {
        this.InitializeComponent();
    }
    
    private void ToggleButton_Click(object sender, RoutedEventArgs e)
    {
        ToggleClick?.Invoke(FoodId);
    }
    

The dependency property that hook to the toggle button also enable OnNotifyPropertyChanged in this code notice that I expose the ToggleButton_Click to let the Page that contain the gridview can handle the click. And here's how I handle it

        private void FoodCard_ToggleClick(int foodId)
    {
        foreach(FoodDTO dto in Foods)
        {
                dto.IsSelected = false;
        }
        foreach (FoodDTO dto in Foods)
        {
            System.Diagnostics.Debug.WriteLine(dto.IsSelected);
            System.Diagnostics.Debug.WriteLine("-------------");
        }
    }

I watch the console they are all output IsSelected is False, but the Toggle Button on the ui element doesn't de-toggle. Here is the FoodDTO

    public class FoodDTO
{
    public int FoodId { get; set; }
    public string FoodName { get; set; }
    public string FoodEnglishName { get; set; }
    public int MainIcon { get; set; } = 5;
    public int? SecondaryIcon { get; set; }
    public decimal Percentage { get; set; }
    public bool IsSelected { get; set; }
}

Upvotes: 1

Views: 53

Answers (1)

Martin Zikmund
Martin Zikmund

Reputation: 39102

The reason the UI does not react to the change in IsSelected property is that the FoodDto is a POCO, and does not implement INotifyPropetyChanged. If you make sure to raise PropertyChanged when IsSelected changes, it will be reflected in the x:Bind in the DataTemplate and will also then flow into your UserControl.

Upvotes: 2

Related Questions