Обновление свойств по клику Custom Control - PullRequest
0 голосов
/ 30 декабря 2018

Я работаю с WPF (в частности, с шаблоном MVVM) и пытаюсь создать простое приложение, которое показывает список задач.Я создал пользовательский элемент управления TaskListControl, который показывает список других пользовательских элементов управления с именем TaskListItemControl, и каждый из них имеет свой собственный ViewModel.

Вот шаблон TaskListItemControl, где вы можете увидеть InputBindings и Triggers, которые влияют на внешний вид элемента управления, когда IsSelected установлено на true:

<UserControl x:Class="CSB.Tasks.TaskListItemControl"
         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:CSB.Tasks"
         mc:Ignorable="d"
         d:DesignHeight="70" 
         d:DesignWidth="400">

<!-- Custom control that represents a Task. -->
<UserControl.Resources>
    <!-- The control style. -->
    <Style x:Key="ContentStyle" TargetType="{x:Type ContentControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ContentControl}">

                    <Border x:Name="ContainerBorder" BorderBrush="{StaticResource LightVoidnessBrush}"
                            Background="{StaticResource VoidnessBrush}"
                            BorderThickness="1"
                            Margin="2">

                        <Border.InputBindings>
                            <MouseBinding MouseAction="LeftClick" Command="{Binding SelctTaskCommand}"/>
                        </Border.InputBindings>

                        <!-- The grid that contains the control. -->
                        <Grid Name="ContainerGrid" Background="Transparent">

                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="Auto"/>
                            </Grid.ColumnDefinitions>

                            <!-- Border representing the priority state of the Task:
                            The color is defined by a ValueConverter according to the PriorityLevel of the Task object. -->
                            <Border Grid.Column="0"
                                    Width="10"
                                    Background="{Binding Priority, Converter={local:PriorityLevelToRGBConverter}}">
                            </Border>

                            <!-- Border containing the Task's informations. -->
                            <Border Grid.Column="1" Padding="5">
                                <StackPanel>
                                    <!-- The title of the Task. -->
                                    <TextBlock Text="{Binding Title}" FontSize="{StaticResource TaskListItemTitleFontSize}" Foreground="{StaticResource DirtyWhiteBrush}"/>

                                    <!-- The customer the Taks refers to. -->
                                    <TextBlock Text="{Binding Customer}" Style="{StaticResource TaskListItemControlCustomerTextBlockStyle}"/>

                                    <!-- The description of the Task. -->
                                    <TextBlock Text="{Binding Description}"
                                               TextTrimming="WordEllipsis"
                                               Foreground="{StaticResource DirtyWhiteBrush}"/>
                                </StackPanel>
                            </Border>

                            <!-- Border that contains the controls for the Task management. -->
                            <Border Grid.Column="2"
                                    Padding="5">

                                <!-- Selection checkbox of the Task. -->
                                <CheckBox Grid.Column="2" VerticalAlignment="Center"/>
                            </Border>

                        </Grid>

                    </Border>

                    <!-- Template triggers. -->
                    <ControlTemplate.Triggers>

                        <DataTrigger Binding="{Binding IsSelected}" Value="True">
                            <Setter Property="Background" TargetName="ContainerBorder" Value="{StaticResource OverlayVoidnessBrush}"/>
                            <Setter Property="BorderBrush" TargetName="ContainerBorder" Value="{StaticResource PeterriverBrush}"/>
                        </DataTrigger>

                        <EventTrigger RoutedEvent="MouseEnter">
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Duration="0:0:0:0" To="{StaticResource OverlayVoidness}" Storyboard.TargetName="ContainerGrid" Storyboard.TargetProperty="Background.Color"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>

                        <EventTrigger RoutedEvent="MouseLeave">
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Duration="0:0:0:0" To="Transparent" Storyboard.TargetName="ContainerGrid" Storyboard.TargetProperty="Background.Color"/>
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                    </ControlTemplate.Triggers>

                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</UserControl.Resources>

<!-- Content of the control: assignment of the DataContext for design-time testing. -->
<ContentControl d:DataContext="{x:Static local:TaskListItemDesignModel.Instance}" 
                Style="{StaticResource ContentStyle}"/>

А вот TaskListItemViewModel, где Action должнобыть выполнено (весь шаблонный код PropertyChanged обрабатывается внутри класса BaseViewModel):

/// <summary>
/// The ViewModel for the <see cref="TaskListItemControl"/>.
/// </summary>
public class TaskListItemViewModel : BaseViewModel
{
    #region Public Properties

    /// <summary>
    /// Priority level of the task.
    /// </summary>
    public PriorityLevel Priority { get; set; }

    /// <summary>
    /// The name of the task.
    /// </summary>
    public string Title { get; set; }

    /// <summary>
    /// The customer the task refers to.
    /// </summary>
    public string Customer { get; set; }

    /// <summary>
    /// The description of the task.
    /// </summary>
    public string Description { get; set; }

    /// <summary>
    /// True if the Task is the selected one in the task list.
    /// </summary>
    public bool IsSelected { get; set; }

    #endregion

    #region Commands

    /// <summary>
    /// The command fired whenever a task is selected.
    /// </summary>
    public ICommand SelectTaskCommand { get; set; }

    #endregion

    #region Constructor

    /// <summary>
    /// The <see cref="TaskListItemViewModel"/> default constructor.
    /// </summary>
    public TaskListItemViewModel()
    {
        // Initialize commands.
        // When the task is selected, IsSelected becomes true.
        // The command will do other stuff in the future.
        SelectTaskCommand = new RelayCommand(() => IsSelected = true);
    }

    #endregion
}

Данные предоставляются через модель проекта, связанную с элементом управления TaskListControl, где свойства каждого элемента спискажестко закодировано (эта модель проекта будет заменена базой данных, поскольку этот класс просто предоставляет фиктивные данные):

/// <summary>
/// The <see cref="TaskListControl"/> design model that provides dummy data for the XAML testing.
/// </summary>
public class TaskListDesignModel : TaskListViewModel
{
    #region Public Properties

    /// <summary>
    /// A single instance of the <see cref="TaskListDesignModel"/> class.
    /// </summary>
    public static TaskListDesignModel Instance => new TaskListDesignModel();

    #endregion

    #region Constructor

    /// <summary>
    /// The <see cref="TaskListDesignModel"/> default constructor that provides dummy data.
    /// </summary>
    public TaskListDesignModel()
    {
        Items = new ObservableCollection<TaskListItemViewModel>
        {
            new TaskListItemViewModel
            {
                Title = "Activity #1",
                Customer = "Internal",
                Description = "This is activity #1.",
                Priority = PriorityLevel.High,
                IsSelected = false
            },

            new TaskListItemViewModel
            {
                Title = "Activity #2",
                Customer = "Internal",
                Description = "This is activity #2.",
                Priority = PriorityLevel.High,
                IsSelected = false
            },

            new TaskListItemViewModel
            {
                Title = "Activity #3",
                Customer = "Internal",
                Description = "This is activity #3.",
                Priority = PriorityLevel.High,
                IsSelected = false
            },

            new TaskListItemViewModel
            {
                Title = "Activity #4",
                Customer = "Internal",
                Description = "This is activity #4.",
                Priority = PriorityLevel.Medium,
                IsSelected = false
            },

            new TaskListItemViewModel
            {
                Title = "Activity #5",
                Customer = "Internal",
                Description = "This is activity #5.",
                Priority = PriorityLevel.Medium,
                IsSelected = false
            },

            new TaskListItemViewModel
            {
                Title = "Activity #6",
                Customer = "Internal",
                Description = "This is activity #6.",
                Priority = PriorityLevel.Low,
                IsSelected = false
            }

        };
    }

    #endregion
}

Вот результат: items_list

Что я хочусделать, когда элемент списка выбран, обновить его свойство IsSelected в ViewModel и изменить его внешний вид с помощью Triggers, но ничего не происходит, когда я нажимаю на элемент.

Здесь - это ссылка на GitHub-репозиторий всего проекта, если необходимо.

Чего мне не хватает?Заранее благодарю за помощь.

1 Ответ

0 голосов
/ 31 декабря 2018

Вы должны решить эти две проблемы, чтобы решить проблему выбора:

1) Я обнаружил опечатку внутри вашего TaskListItemControl: строка 25 должна быть "Sel e ctTaskCommand" наПривязка команд.Это, наконец, вызовет команду.

2) Затем в вашем TaskListItemViewModel вы должны заставить свои свойства вызывать событие PropertyChanged.Я настоятельно рекомендую это для всех ваших свойств ViewModel.Но для решения вашей проблемы это должно применяться по крайней мере к свойству IsSelected:

private bool isSelected;
public bool IsSelected 
{ 
  get => this.isSelected; 
  set 
  { 
    if (value.Equals(this.isSelected)
    {
      return;
    }

    this.isSelected = value;  
    OnPropertyChanged(); 
  }
}

Это внесет любые изменения в распространение IsSelected и, таким образом, DataTrigger может работать так, как ожидается.

И еще одна рекомендация - изменить сигнатуру PropertyChanged в BaseViewModel на:

public void OnPropertyChanged([CallerMemberName] string propertyName = null)

Таким образом, вы всегда избегаете передачи имени свойства в качестве аргумента.

Есливы хотите, чтобы CheckBox отражал состояние выбора и должен использоваться для отмены выбора, просто добавьте привязку TwoWay к свойству IsChecked:

<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}" />
...