简体   繁体   中英

ItemsControl view doesn't updated when an item in ObservableCollection is updated

Introduction :

Hi, I meet a weird problem here. My ItemsControl doesn't update the view if I am updating the model (eg changing the value of IsSelected ).

One of IsSelected purpose however, is, if the value is true , then the Background of the MusicalNotationBox (UserControl) is changed to blue, and if it's false then it's changed back to transparent.

A lot of person asked : Why not using trigger such as Focus for IsSelected ? Because it's not just for "visual" purposes. I have a Command which to change certain Property (for example Note's Octave) of each MusicalNotation object in VM's MusicalNotations , which IsSelected==true (support multiselection), so I think I need IsSelected in the model. But this is not the problem here, so please no answer solely on this matter.

The problem is :

  1. The model property is changed successfully (checked and verified), but it seems that the view isn't .
  2. If I use the singular form of the UserControl, for eg <c:MusicalNotationBox DataContext={Binding}/> (ofc along with it's friends aka correct properties in the VM), it synced perfectly . So, I'm not quite sure where the problem lies with the OC.
  3. UPDATE : If I, for example create a MouseBinding that each time I click, then a new MusicalNotation added to the list, then, the view is also updated.

I have read several topic (google and here) on "Observable Collection doesn't update the ItemsControl`, but still found no satisfying answer.

Here's my code (code may trimmed (...) for clarity sake) :

MODEL

public class MusicalNotation : ... INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    ...
    private bool _isSelected;
    ...
    public bool IsSelected
    {
        get { return _isSelected; }
        set { _isSelected = value; NotifyPropertyChanged("IsSelected"); }
    }
    ...
    public MusicalNotation()
    {
        ...
        IsSelected = false;
    }
    ...
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

VIEW MODEL

public class MainWindowModelView : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    private ObservableCollection<MusicalNotation> _musicalNotations;
    ...
    public ObservableCollection<MusicalNotation> MusicalNotations
    {
        get { return _musicalNotations; }
        set { _musicalNotations = value; NotifyPropertyChanged("MusicalNotations"); }
    }

    public MainWindowModelView()
    {
        ...
        MusicalNotations = new ObservableCollection<MusicalNotation>();

        //Direct initialization for testing purpose
        MusicalNotations.Add(MusicalNotation.GetEmptyNote(new TimeSignature(4, 4)));
        MusicalNotations.Add(MusicalNotation.GetEmptyNote(new TimeSignature(4, 4)));

        foreach (MusicalNotation item in MusicalNotations)
        {
            item.IsSelected = true;
        }
    }

    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

VIEW

<Window ...>
<Window.Resources>

</Window.Resources>
<Window.InputBindings>
    ...
</Window.InputBindings>
<Grid>
    ...        
    <ItemsControl Grid.Column="0" Grid.Row="0" ItemsSource="{Binding MusicalNotations, Mode=OneWay}"
                  HorizontalAlignment="Center" VerticalAlignment="Center">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <c:MusicalNotationBox/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

Thanks.

UPDATE, My MusicalNotationBox XAML

<UserControl x:Class="NumberedMusicScoresUserControl.MusicalNotationBox"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:NumberedMusicScoresUserControl.MusicalNotationBoxProperties"
         mc:Ignorable="d">
<UserControl.Resources>
    <local:DotConverter x:Key="DotConverter"/>
    <local:NoteConverter x:Key="NoteConverter"/>
    <local:AccidentalConverter x:Key="AccidentalConverter"/>
    <local:IsSelectedConverter x:Key="IsSelectedConverter"/>
</UserControl.Resources>

<Grid x:Name="grid" Margin="10,5,10,5"
      HorizontalAlignment="Center" VerticalAlignment="Center"
      Background="{Binding Path=MusicalNotation.IsSelected, Converter={StaticResource IsSelectedConverter}, Mode=OneWay}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <TextBlock Grid.Column="0" Grid.Row="1"
               Text="b"
               Visibility="{Binding Path=MusicalNotation.Accidental, Converter={StaticResource AccidentalConverter}, ConverterParameter=FL, Mode=OneWay}" 
               FontSize="15" FontFamily="CourierNew" 
               HorizontalAlignment="Center" VerticalAlignment="Center"/>
    <Path Grid.Column="1" Grid.Row="1" Stroke="Black" StrokeThickness="1" Stretch="Fill"
          Visibility="{Binding Path=MusicalNotation.Accidental, Converter={StaticResource AccidentalConverter}, ConverterParameter=SP, Mode=OneWay}" >
        <Path.Data>
            <LineGeometry StartPoint="1,0" EndPoint="0,1">
                <LineGeometry.Transform>
                    <RotateTransform CenterX="0" CenterY="0" Angle="30"/>
                </LineGeometry.Transform>
            </LineGeometry>
        </Path.Data>
    </Path>
    <TextBlock Grid.Column="1" Grid.Row="1" 
               Text="{Binding Path=MusicalNotation.Note, Converter={StaticResource NoteConverter}, Mode=OneWay}" 
               FontSize="15" FontFamily="CourierNew" 
               HorizontalAlignment="Center" VerticalAlignment="Center"
               Margin="2.5,0,2.5,0"/>
    <ItemsControl Grid.Column="1" Grid.Row="0" 
                  ItemsSource="{Binding Path=MusicalNotation.Octave, Converter={StaticResource DotConverter}, ConverterParameter=TOP, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Ellipse HorizontalAlignment="Center" VerticalAlignment="Top"
                         Margin="{Binding Margin}" Fill="{Binding Fill}" 
                         Width="{Binding Diameter}" Height="{Binding Diameter}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ItemsControl Grid.Column="1" Grid.Row="2" 
                  ItemsSource="{Binding Path=MusicalNotation.Octave, Converter={StaticResource DotConverter}, ConverterParameter=BOT, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Ellipse HorizontalAlignment="Center" VerticalAlignment="Bottom"
                         Margin="{Binding Margin}" Fill="{Binding Fill}" 
                         Width="{Binding Diameter}" Height="{Binding Diameter}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
    <ItemsControl Grid.Column="2" Grid.Row="1" 
                  ItemsSource="{Binding Path=MusicalNotation.Dot, Converter={StaticResource DotConverter}, ConverterParameter=RIGHT, Mode=OneWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal" IsItemsHost="True"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Ellipse HorizontalAlignment="Left" VerticalAlignment="Center"
                         Margin="{Binding Margin}" Fill="{Binding Fill}" 
                         Width="{Binding Diameter}" Height="{Binding Diameter}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

(I didn't include it since, I think, if the "singular" one is correct, then my UserControl is correct as well. Might be wrong though.)

The DataContext for your user control is the MusicalNotation object, so while binding instead of using

Background="{Binding Path=MusicalNotation.IsSelected, Converter={StaticResource ....

just use

Background="{Binding Path=IsSelected, Converter={StaticResource ....

As you haven't specified any binding for the selected item or for the IsSelected in your xaml, that's why view is not being updated when the property is changed in the VM. For updating the view you will have to provide a binding for SelectedItem property of ItemsControl.

从绑定中删除MusicalNotation前缀。

{Binding Path=IsSelected, ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM