WPF - MVVM - Combobox UserControl + ComboboxItem CustomControl - PullRequest
0 голосов
/ 12 июня 2018

Я изо всех сил пытаюсь создать привязку из View + ViewModel к Custom ComboboxItem.Я не уверен, что это должно работать, я уже реализовал сложные UserControls и должен был правильно настроить DataContext, чтобы он работал с MVVM, но этот конкретный сценарий вообще не работает.

Что я пытаюсь сделать:

Создание пользовательского элемента управления на основе Combobox.Это будет иметь поведение Combobox, но оно настроено.Свернутый список со списком покажет только кнопку без Путь, а при раскрытии (показывает выпадающий список элементов со списком) он покажет больше настроенных кнопок.Этот UserControl должен работать с MVVM (который сейчас не работает).Если я устанавливаю содержимое ComboboxItem в жестко запрограммированном представлении View, оно отображается правильно, но если я пытаюсь выполнить привязку с viewmodel, происходит сбой:

System.Windows.Data Ошибка: 40: Ошибка пути BindingExpression: Свойство 'MessageName' не найдено в 'object' '' MultiButtonControl '(Name =' ')'.BindingExpression: Path = MessageName;DataItem = 'MultiButtonControl' (Name = '');целевой элемент - «TestComboBoxItem» (Name = '');целевым свойством является 'Content' (тип 'Object')

Я создал:

  • Пользовательский контроль MultiButtonControl, содержащий сетку и комбинированный список с определенным стилем иСписок с именем Children в коде позади (свойство зависимости).Я создаю привязку из выпадающего списка на xaml со свойством Children.

  • Пользовательский элемент управления TestComboBoxItem.cs, который расширяется из ComboBoxItem.И я создал в Generic.xaml стиль для этого типа.

DataContext:

Я установил DataContext из UserControl "MultiButtonControl.xaml"на родительском элементе -> элемент сетки как:

<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">

В настоящее время работает:

Прямо сейчас макет работает нормально.Я могу использовать свой UserControl в представлении, добавить детей.Но проблема в связывании.Я считаю, что проблема в DataContext.Потому что, когда я пытаюсь связать свойство из ComboboxItem (пользовательский элемент управления TestComboBoxItem), оно выходит из строя, и если я установил его жестко, оно работает.

Позвольте мне дать вам код:

View.xaml

<Window x:Class="Test2Manager.message.ModalMessageInsert"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Test2Manager.message"
    xmlns:controls="clr-namespace:CSIncludes.controls;assembly=CSIncludes"
    xmlns:wpf="clr-namespace:CSIncludes.wpf;assembly=CSIncludes"
    mc:Ignorable="d"
    WindowStyle="None"
    AllowsTransparency="True"
    WindowStartupLocation="CenterOwner" 
    WindowState="Maximized"
    Background="#33000000"
    Title="ModelMessageInsert"
    Name="ModalWindow">
<Window.Resources>
    <ResourceDictionary Source="/CSIncludes;component/Themes/Generic.xaml"/>
</Window.Resources>
<Grid Width="600" Height="400" VerticalAlignment="Center" HorizontalAlignment="Center" Background="Gray">
    <Grid.RowDefinitions>
        <RowDefinition Height="30"></RowDefinition>
        <RowDefinition Height="*"></RowDefinition>
        <RowDefinition Height="30"></RowDefinition>
    </Grid.RowDefinitions>
    <TextBlock Grid.Row="0"></TextBlock>
    <Grid Grid.Row="1" Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="30"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
            <ColumnDefinition Width="10"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">Name:</TextBlock>
        <TextBox Grid.Row="0" Grid.Column="2" VerticalAlignment="Center" Text="{Binding MessageName, UpdateSourceTrigger=PropertyChanged}">
            <TextBox.Effect>
                <DropShadowEffect Color="Yellow" Direction="270" ShadowDepth="2" Opacity="1" BlurRadius="2"></DropShadowEffect>
            </TextBox.Effect>

        </TextBox>
        <!--<controls:AudioControl Grid.Row="2" Grid.Column="2" RecordingMode="False" ReproduceAudioPath="C:\Fabio\Musicas\05 - On The Turning Away.mp3"></controls:AudioControl>-->
        <controls:MultiButtonControl Grid.Row="2" Grid.Column="2" Width="170" ParentButtonText="{Binding MessageName}" ParentButtonImage="/CSIncludes;component/images/audio_play.png">
            <controls:MultiButtonControl.Children>
                <!--Command2="{Binding TestCommand}"-->
                <wpf:TestComboBoxItem Content="{Binding MessageName}"></wpf:TestComboBoxItem>
                <wpf:TestComboBoxItem Content="Fabio 2"></wpf:TestComboBoxItem>
                <wpf:TestComboBoxItem>Fabio 3</wpf:TestComboBoxItem>
            </controls:MultiButtonControl.Children>
        </controls:MultiButtonControl>
        <!--<ComboBox Style="{StaticResource CustomCombobox}">
            <ComboBoxItem>Item1</ComboBoxItem>
            <ComboBoxItem>Item2</ComboBoxItem>
            <ComboBoxItem>Item3</ComboBoxItem>
            <ComboBoxItem>Item4</ComboBoxItem>
            <ComboBoxItem>Item5</ComboBoxItem>
        </ComboBox>-->
    </Grid>
    <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Center" Margin="5">
        <Button Width="65" Command="{Binding SaveCommand}" CommandParameter="{Binding ElementName=ModalWindow}">Save</Button>
        <Button Width="65" Command="{Binding CancelCommand}" CommandParameter="{Binding ElementName=ModalWindow}" Margin="5,0,0,0">Cancel</Button>
    </StackPanel>
</Grid>

View - настройка кода для DataContext для ViewModel

    using Arbeit.wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace Test2Manager.message
{
    /// <summary>
    /// Interaction logic for ModelMessageInsert.xaml
    /// </summary>
    public partial class ModalMessageInsert : Window
    {
        ViewModelModalMessageInsert vm;
        public ModalMessageInsert(Test2Entities Context, Action UpdateList)
        {
            InitializeComponent();
            vm = new ViewModelModalMessageInsert(Context, UpdateList);
            DataContext = vm;
        }
    }
}

ViewModel

    using CSIncludes.wpf;
using Test2EF;
using Test2Manager.database;
using Test2Manager.manager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace Test2Manager.message
{
    class ViewModelModalMessageInsert
    {
        private Test2Entities Context;
        private MessageDAL MessageDAL;
        private Action UpdateList;
        private ModelMessage Model;

        public ViewModelModalMessageInsert(Test2Entities Context, Action UpdateList)
        {
            this.Context = Context;
            this.UpdateList = UpdateList;
            MessageDAL = new MessageDAL(Context);
            Model = new ModelMessage();
            Model.ClientId = LoggedManager.ClientId;

            MessageName = "Teste";
        }

        public string MessageName
        {
            get { return Model.Name; }
            set
            {
                Model.Name = value;
            }
        }
    }
}

MultiButtonControl.xaml

<UserControl x:Class="CSIncludes.controls.MultiButtonControl"
         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:CSIncludes.controls"
         xmlns:wpf="clr-namespace:CSIncludes.wpf"
         mc:Ignorable="d" 
         d:DesignHeight="55" d:DesignWidth="300">
<UserControl.Resources>
    <!--<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Border
              x:Name="Border" 
              CornerRadius="0"
              Background="#FF3F3F3F"
              BorderBrush="#FF97A0A5"
              BorderThickness="1" />
        </Grid>
    </ControlTemplate>-->

    <ControlTemplate x:Key="ComboBoxTextBox" TargetType="{x:Type TextBox}">
        <Border x:Name="PART_ContentHost" Focusable="False" Background="{TemplateBinding Background}" />
    </ControlTemplate>

    <Style x:Key="CustomCombobox" TargetType="{x:Type ComboBox}">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
        <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
        <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
        <Setter Property="MinWidth" Value="120"/>
        <Setter Property="MinHeight" Value="30"/>
        <Setter Property="Foreground" Value="White"/>
        <!--<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></Setter>-->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ComboBox}">
                    <Grid>
                        <!--Template="{StaticResource ComboBoxToggleButton}"-->
                        <ToggleButton
                            VerticalAlignment="Center"
                            Height="{Binding ParentButtonHeight}" 
                            Name="ToggleButton" 
                            Focusable="false"
                            IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
                            ClickMode="Press">

                            <ToggleButton.Style>
                                <Style TargetType="{x:Type ToggleButton}">
                                    <Setter Property="Background" Value="#5F1E78"></Setter>
                                    <Setter Property="BorderBrush" Value="#5F1E78"></Setter>
                                    <Setter Property="BorderThickness" Value="1"></Setter>
                                    <Setter Property="Template">
                                        <Setter.Value>
                                            <ControlTemplate TargetType="{x:Type ToggleButton}">
                                                <Border BorderBrush="#FF97A0A5" BorderThickness="1" HorizontalAlignment="Center" Width="{TemplateBinding ActualWidth}">
                                                    <Grid Background="{TemplateBinding Background}" HorizontalAlignment="Center" Width="{TemplateBinding ActualWidth}">
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="*"></ColumnDefinition>
                                                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                                                            <ColumnDefinition Width="Auto"></ColumnDefinition>
                                                            <ColumnDefinition Width="*"></ColumnDefinition>
                                                        </Grid.ColumnDefinitions>
                                                        <TextBlock Grid.Column="1" Margin="0,0,8,0" Foreground="White" Name="Text" Text="{Binding ParentButtonText}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                                        <Image Grid.Column="2" Name="Image" Width="16" Height="16" Source="{Binding ParentButtonImage}" />
                                                    </Grid>
                                                    <!--<Grid>
                                                        <Grid.RowDefinitions>
                                                            <RowDefinition Height="Auto" />
                                                        </Grid.RowDefinitions>
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="Auto" />
                                                            <ColumnDefinition Width="16" />
                                                        </Grid.ColumnDefinitions>
                                                        <TextBlock Margin="10" Foreground="White" Grid.Column="0" Name="Text" Text="{Binding ParentButtonText}" HorizontalAlignment="Center" />
                                                        <Image Grid.Column="1" Name="Image" Source="{Binding ParentButtonImage}" />
                                                    </Grid>-->
                                                </Border>
                                                <!--<ControlTemplate.Triggers>
                                                    <Trigger Property="IsChecked" Value="True">
                                                        <Setter TargetName="Text" Property="Foreground" Value="White" />
                                                    </Trigger>
                                                </ControlTemplate.Triggers>-->
                                            </ControlTemplate>
                                        </Setter.Value>
                                    </Setter>
                                </Style>
                            </ToggleButton.Style>
                        </ToggleButton>
                        <!--<ToggleButton Content="aaa"
                            Name="ToggleButton" 
                            Template="{StaticResource ComboBoxToggleButton}" 
                            Grid.Column="2" 
                            Focusable="false"
                            IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
                            ClickMode="Press">
                        </ToggleButton>-->
                        <TextBox x:Name="PART_EditableTextBox" Style="{x:Null}" Height="{Binding ParentButtonHeight}" Template="{StaticResource ComboBoxTextBox}"  HorizontalAlignment="Left" 
                                VerticalAlignment="Center" Focusable="True" Background="White" Foreground="Black" Visibility="Hidden" IsReadOnly="{TemplateBinding IsReadOnly}"/>
                        <Popup Name="Popup" Placement="Top" PlacementTarget="{Binding ElementName=PART_EditableTextBox}" IsOpen="{TemplateBinding IsDropDownOpen}" AllowsTransparency="True"  Focusable="False" PopupAnimation="Slide">
                            <Grid Name="DropDown" SnapsToDevicePixels="True" MinWidth="{TemplateBinding ActualWidth}" Width="{TemplateBinding ActualWidth}" MaxHeight="{TemplateBinding MaxDropDownHeight}" Height="{TemplateBinding ActualHeight}">
                                <Border  x:Name="DropDownBorder" Background="White" BorderThickness="1" BorderBrush="#888888"/>
                                <ScrollViewer Margin="0,0,0,0" SnapsToDevicePixels="True">
                                    <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                                </ScrollViewer>
                            </Grid>
                        </Popup>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="HasItems" Value="false">
                            <Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="#888888"/>
                        </Trigger>
                        <Trigger Property="IsGrouping" Value="true">
                            <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                        </Trigger>
                        <Trigger SourceName="Popup" Property="Popup.AllowsTransparency" Value="true">
                            <Setter TargetName="DropDownBorder" Property="CornerRadius" Value="0"/>
                            <Setter TargetName="DropDownBorder" Property="Margin" Value="0,0,0,0"/>
                        </Trigger>
                        <Trigger Property="IsEditable"  Value="true">
                            <Setter Property="IsTabStop" Value="false"/>
                            <Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
                            <!--<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>-->
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</UserControl.Resources>

<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}">
    <ComboBox Style="{StaticResource CustomCombobox}" Focusable="False" x:Name="ComboBox" ItemsSource="{Binding Children}">
    </ComboBox>
</Grid>

MultiButtonControl.xaml.cs

    using CSIncludes.wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CSIncludes.controls
{
    /// <summary>
    /// Interaction logic for MultiButtonControl.xaml
    /// </summary>
    public partial class MultiButtonControl : UserControl
    {
        public MultiButtonControl()
        {
            InitializeComponent();

            this.Loaded += UserControl_Loaded;
            Children = new List<TestComboBoxItem>();
        }

        public static readonly DependencyProperty ParentButtonTextProperty = DependencyProperty.Register(
        "ParentButtonText",
        typeof(string),
        typeof(MultiButtonControl));

        public static readonly DependencyProperty ParentButtonImageProperty = DependencyProperty.Register(
        "ParentButtonImage",
        typeof(ImageSource),
        typeof(AudioControl),
        new UIPropertyMetadata(null));

        public static readonly DependencyProperty ParentButtonHeightProperty = DependencyProperty.Register(
        "ParentButtonHeight",
        typeof(double),
        typeof(MultiButtonControl));

        public static readonly DependencyProperty ChildButtonHeightProperty = DependencyProperty.Register(
                "ChildButtonHeight",
                typeof(double),
                typeof(MultiButtonControl));

        public static readonly DependencyProperty ChildrenProperty = DependencyProperty.Register(
                "Children",
                typeof(List<TestComboBoxItem>),
                typeof(MultiButtonControl));

        public string ParentButtonText
        {
            get { return (string)GetValue(ParentButtonTextProperty); }
            set { SetValue(ParentButtonTextProperty, value); }
        }

        public ImageSource ParentButtonImage
        {
            get { return (ImageSource)GetValue(ParentButtonImageProperty); }
            set { SetValue(ParentButtonImageProperty, value); }
        }

        public double ParentButtonHeight
        {
            get { return (double)GetValue(ParentButtonHeightProperty); }
            set { SetValue(ParentButtonHeightProperty, value); }
        }

        public double ChildButtonHeight
        {
            get { return (double)GetValue(ChildButtonHeightProperty); }
            set { SetValue(ChildButtonHeightProperty, value); }
        }

        public List<TestComboBoxItem> Children
        {
            get { return (List<TestComboBoxItem>)GetValue(ChildrenProperty); }
            set { SetValue(ChildrenProperty, value); }
        }

        void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (ParentButtonHeight == 0)
                ParentButtonHeight = 35;

            if (ChildButtonHeight == 0)
                ChildButtonHeight = 25;

            ComboBox.MaxDropDownHeight = ComboBox.Items.Count * ChildButtonHeight;
        }

    }
}

Generic.xaml

    <Style x:Key="{x:Type wpf:TestComboBoxItem}" TargetType="{x:Type wpf:TestComboBoxItem}">
        <Setter Property="ComboBoxItem.SnapsToDevicePixels" Value="true"/>
        <Setter Property="ComboBoxItem.Foreground" Value="Black"/>
        <Setter Property="ComboBoxItem.OverridesDefaultStyle" Value="true"/>
        <Setter Property="ComboBoxItem.Height" Value="{Binding ChildButtonHeight}"/>
        <Setter Property="ComboBoxItem.VerticalAlignment" Value="Center"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TestComboBoxItem}">
                    <Border Name="Border"  Padding="5" SnapsToDevicePixels="true">
                        <ContentPresenter />
                    </Border>

                    <ControlTemplate.Triggers>
                        <Trigger Property="IsHighlighted" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="#7E59F2"/>
                            <Setter Property="Foreground" Value="White"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter Property="Foreground" Value="#888888"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

TestComboBoxItem.cs

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace CSIncludes.wpf
{
    public class TestComboBoxItem : ComboBoxItem
    {
        static TestComboBoxItem()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TestComboBoxItem), new FrameworkPropertyMetadata(typeof(ComboBoxItem)));
        }

        public static DependencyProperty CommandParameter2Property =
        DependencyProperty.Register("CommandParameter2", typeof(object), typeof(TestComboBoxItem));

        public static DependencyProperty Command2Property =
            DependencyProperty.Register("Command2", typeof(ICommand), typeof(TestComboBoxItem));

        public static DependencyProperty ItemTextProperty =
                    DependencyProperty.Register("ItemText", typeof(string), typeof(TestComboBoxItem));

        public ICommand Command2
        {
            get { return (ICommand)GetValue(Command2Property); }
            set { SetValue(Command2Property, value); }
        }

        public object CommandParameter2
        {
            get { return GetValue(CommandParameter2Property); }
            set { SetValue(CommandParameter2Property, value); }
        }

        public string ItemText
        {
            get { return (string)GetValue(ItemTextProperty); }
            set { SetValue(ItemTextProperty, value); }
        }
    }
}

Есть ли у вас какие-либоключ к пониманию того, как исправить привязки TestComboBoxItem?Как бы вы сделали, чтобы это работало?После привязки Контента мне нужно будет также создать привязки с ICommand.

...