Команда элемента не может редактировать другой элемент в списке - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть боковая панель (в программе C # WPF), на которой должны отображаться 4 «разные» кнопки (на самом деле это 2 разных стиля, у каждого из которых есть другой стиль для активного состояния).Боковая панель состоит из ItemsControl.Теперь мне удалось создать список, в котором используется правильный стиль на основе значения перечисления (как показано ниже).Вот небольшой вопрос: могу ли я сделать это таким образом, или я должен переписать это, и если это так, как можно построить что-то подобное?Мне достаточно ключевых слов или чего-то, на что я должен смотреть.

Мой настоящий вопрос сейчас таков: я привязал команду к каждой кнопке, сначала ничего сложного.Команда теперь устанавливает свое собственное состояние в NormalActive для целей тестирования.Первый элемент в этом списке должен быть изменен с LiveActive на Live (чтобы вы всегда видели текущий выбранный элемент в том виде, в котором вы его знаете).И вот проблема: кнопка может установить свое собственное состояние, поэтому, когда я нажимаю на кнопку 3, состояние кнопки 3 устанавливается с Normal на NormalActive.Но чего не происходит, так это перехода с LiveActive на Active с 1-й кнопки.Даже если я вывожу текущее состояние на консоль до и после изменения, он возвращает LiveActive для обоих.Я также попытался вызвать все это в диспетчере, если я по какой-то причине не в потоке пользовательского интерфейса, это не сработало.Таким образом, кнопка может установить свое собственное состояние, но не одно из другого.Но я не получаю сообщение об ошибке или что-то еще.Также вызывается метод установки свойства, он просто не меняет его.В чем может быть причина?

PluginListControl:

<Grid DataContext="{x:Static local:PluginListDesignModel.Instance}">
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:PluginListItemControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Grid>

PluginListItemControl:

<UserControl.Resources>
    <DataTemplate x:Key="PluginTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginActiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginActiveLiveTile" DataType="{x:Type local:PluginListItemViewModel}">
        <Button Style="{StaticResource PluginActiveLiveTile}" Content="{Binding DataContext.Name, RelativeSource={RelativeSource AncestorType=ContentControl}}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>
</UserControl.Resources>
<ContentControl d:DataContext="{x:Static local:PluginListItemDesignModel.Instance}">

    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding State}" Value="0">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="1">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="2">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginLiveTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="3">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginActiveLiveTile}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>

</ContentControl>

PluginListItemViewModel: (ViewModel для каждого элемента списка)

public class PluginListItemViewModel : BaseViewModel
{
    public string Name { get; set; }
    public PluginTileStates State { get; set; }
    public ICommand SetStateCommand { get; set; }

    #region Constructor

    /// <summary>
    /// Default constructor
    /// </summary>
    public PluginListItemViewModel()
    {
        SetStateCommand = new RelayCommand(() => SetState());
    }

    #endregion

    private void SetState()
    {
        PluginListDesignModel.Instance.Items[0].State = PluginTileStates.Live;
        State = PluginTileStates.NormalActive;
    }
}

Шаги для воспроизведения:

  1. Создание нового проекта WPF, .NET Framework 4.6.1 (Visual Studio 2017).
  2. Замените сетку в главном окне следующим:

<Grid DataContext="{x:Static local:ListViewModel.Instance}">
    <ScrollViewer VerticalScrollBarVisibility="Auto">
        <ItemsControl ItemsSource="{Binding Items}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:ListItemControl />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Grid>
Добавьте новый UserControl с именем ListItemControl и замените сетку на:
<UserControl.Resources>
    <Style x:Key="Tile" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Red" />
    </Style>

    <Style x:Key="ActiveTile" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Green" />
    </Style>

    <DataTemplate x:Key="PluginTile" DataType="{x:Type local:ListItemViewModel}">
        <Button Width="100" Height="60" Style="{StaticResource Tile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>

    <DataTemplate x:Key="PluginActiveTile" DataType="{x:Type local:ListItemViewModel}">
        <Button Width="100" Height="60" Style="{StaticResource ActiveTile}" Command="{Binding DataContext.SetStateCommand, RelativeSource={RelativeSource AncestorType=ContentControl}}" />
    </DataTemplate>
</UserControl.Resources>
<ContentControl>
    <ContentControl.Style>
        <Style TargetType="{x:Type ContentControl}">
            <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding State}" Value="0">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginTile}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="1">
                    <Setter Property="ContentTemplate" Value="{StaticResource PluginActiveTile}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>
Добавьте новый класс с именем BaseViewModel и замените класс следующим:
public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

    public void OnPropertyChanged(string name)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}
Добавить новый класс с именем ListItemViewModel и заменить класс следующим:
public enum TileStates
{
    Normal = 0,
    Active = 1
}

public class ListItemViewModel : BaseViewModel
{
    public TileStates State { get; set; }

    public ICommand SetStateCommand { get; set; }

    public ListItemViewModel()
    {
        SetStateCommand = new RelayCommand(() =>
          {
              ListViewModel.Instance.Items[0].State = TileStates.Normal;
              State = TileStates.Active;
          });
    }
}
Добавьте новый класс с именем ListViewModel и замените класс следующим:
public class ListViewModel : BaseViewModel
{
    public static ListViewModel Instance => new ListViewModel();

    public List<ListItemViewModel> Items { get; set; } = new List<ListItemViewModel>
    {
        new ListItemViewModel
        {
            State = TileStates.Active
        },
        new ListItemViewModel
        {
            State = TileStates.Normal
        }
    };
}
Добавьте новый класс с именем RelayCommand и замените класс следующим:
public class RelayCommand : ICommand
{
    private Action mAction;

    public event EventHandler CanExecuteChanged = (sender, e) => { };

    public RelayCommand(Action action)
    {
        mAction = action;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        mAction();
    }
}
Установите пакеты NuGet: «Fody v4.0.2» и «PropertyChanged.Fody v2.6.0» (возможно, вам придется перезапустить Visual Studio после установки

Если вы сейчас нажмете нижнюю кнопку, он должен стать зеленым, а верхний - красным.

1 Ответ

0 голосов
/ 22 февраля 2019

ListViewModel.Instance возвращает новый экземпляр класса ListViewModel каждый раз, когда он вызывается.Он должен вернуть тот же экземпляр:

public static ListViewModel Instance { get; } = new ListViewModel();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...