WPF - MVVM - Combobox UserControl + ComboboxItem CustomControl

时间:2018-06-12 16:41:47

标签: xaml mvvm binding datacontext

我正在努力创建从View + ViewModel到Custom ComboboxItem的绑定。我不确定它是否应该工作,我已经实现了复杂的UserControls并且必须正确处理DataContext以使其与MVVM一起工作,但是这个特定的场景根本不起作用。

我要做的是:

基于Combobox创建用户控件。它将具有Combobox行为,但它是自定义的。组合框折叠后只显示一个没有Path的按钮,当展开(显示组合框项目的下拉列表)时,它将显示更多自定义的按钮。 此UserControl必须与MVVM(现在无法正常工作)一起使用。如果我在View硬编码上设置ComboboxItem的内容,它们会正确显示,但如果我尝试使用viewmodel进行绑定,则会失败:

  

System.Windows.Data错误:40:BindingExpression路径错误:'object'上找不到'MessageName'属性'''MultiButtonControl'(Name ='')'。 BindingExpression:路径= MessageName; DataItem ='MultiButtonControl'(Name =''); target元素是'TestComboBoxItem'(Name =''); target属性是'Content'(类型'Object')

我创建了:

  • 一个usercontrol MultiButtonControl包含一个网格和一个具有特定样式的组合框和一个名为Children的代码隐藏在后面(依赖属性)。我使用Children属性从xaml上的组合框创建绑定。

  • 从ComboBoxItem扩展的自定义控件TestComboBoxItem.cs。我在Generic.xaml上创建了该类型的样式。

的DataContext:

我在父元素上设置UserControl“MultiButtonControl.xaml”的DataContext - >网格元素为:

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

目前有效:

现在布局工作正常。我可以在View中使用我的UserControl,也可以添加Children。但问题是绑定。我相信问题是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>

查看 - 代码隐藏设置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;
        }
    }
}

视图模型

    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创建绑定。

0 个答案:

没有答案