Представление ItemsControl не обновляется при обновлении элемента в ObservableCollection

#c# #wpf #mvvm #observablecollection

#c# #wpf #mvvm #observablecollection

Вопрос:

Введение :

Привет, я столкнулся здесь со странной проблемой. My ItemsControl не обновляет представление, если я обновляю модель (например, меняю значение IsSelected ).

Однако одна из IsSelected целей заключается в том, что если значение равно true , то фон MusicalNotationBox (UserControl) изменяется на синий, а если это false так, то он снова становится прозрачным.

Многие спрашивали: почему бы не использовать триггер, такой как Focus for IsSelected ? Потому что это не только для «визуальных» целей. У меня есть команда, которая изменяет определенные Property (например, октаву ноты) каждого MusicalNotation объекта в виртуальных MusicalNotations машинах, которые IsSelected==true (поддерживают множественный выбор), поэтому я думаю, что мне нужно IsSelected в модели. Но проблема здесь не в этом, поэтому, пожалуйста, не отвечайте исключительно по этому вопросу.

Проблема заключается в :

  1. Свойство модели успешно изменено (проверено и проверено), но, похоже, представление не изменено.
  2. Если я использую единственную форму UserControl, например <c:MusicalNotationBox DataContext={Binding}/> (ofc вместе с его друзьями, также известными как правильные свойства в виртуальной машине), он отлично синхронизируется. Итак, я не совсем уверен, в чем проблема с операционной системой.
  3. ОБНОВЛЕНИЕ: если я, например, создаю привязку мыши, которая каждый раз, когда я нажимаю, MusicalNotation добавляется в список, тогда представление также обновляется.

Я прочитал несколько тем (Google и здесь) на тему «Наблюдаемая коллекция не обновляет ItemsControl», но по-прежнему не нашел удовлетворительного ответа.

Вот мой код (код может быть обрезан (…) для ясности) :

Модель

 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));
        }
    }
 

ПРОСМОТР МОДЕЛИ

 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));
        }
    }
}
 

Вид

 <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>
 

Спасибо.

ОБНОВЛЕНИЕ, мой 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>
 

(Я не включил его, поскольку, я думаю, если «единственное число» правильное, то и мой UserControl тоже правильный. Хотя это может быть неправильно.)

Комментарии:

1. Нет никакой привязки IsSelected . Какой код для datatemplate?

2. Обновлено @CameronMacFarland

Ответ №1:

DataContext Для вашего пользовательского элемента управления является MusicalNotation объект, поэтому при привязке вместо использования

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

просто используйте

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

Ответ №2:

Поскольку вы не указали никакой привязки для выбранного элемента или для выбранного в вашем xaml, поэтому представление не обновляется при изменении свойства в виртуальной машине. Для обновления представления вам необходимо будет предоставить привязку для свойства SelectedItem элемента ItemsControl.

Комментарии:

1. это пользовательский элемент управления, поэтому такая привязка выполняется в коде пользовательского элемента управления. Но теперь я также включил свой usercontrol xaml. Пожалуйста, проверьте это.

Ответ №3:

Удалите префикс MusicalNotation из привязок.

 {Binding Path=IsSelected, ...
 

Комментарии:

1. IsSelected находится в MusicalNotation классе. Во всяком случае, я пробовал, и никаких изменений.