尝试将资源模板绑定到用户控件

时间:2017-08-21 12:10:13

标签: c# wpf xaml mvvm

我知道使用模板方法来设置用户控件的样式。

这是一个自定义菜单控件。它暂时实现为列表视图。

我希望我的用户控件具有不同的样式,具体取决于我在应用程序中的使用位置。该应用程序将有一个顶级菜单,右侧菜单和一个底部菜单,至少这是现在的想法。我想对所有菜单使用相同的用户控件,但我也希望在不使用不同版本的用户控件的情况下对它们进行不同的设置。

所以我的方法是在资源/样式文件中定义不同的ControlTemplates。这些ControlTemplates在主应用程序xaml中用于对menucontainer用户控件的SelectedTemplate依赖项属性进行绑定。

完整的用户控制代码如下。这是感兴趣的Field SelectedTemplate:

<UserControl x:Class="MyApp.Views.Menu.CustomMenuContainer"
         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:viewModels="clr-namespace:MyApp.Client.ViewModels"
         xmlns:local="clr-namespace:MyApp.Client.Views"
         xmlns:commongui="clr-namespace:MyApp.CommonGui;assembly=MyApp.CommonGui"
         xmlns:menu="clr-namespace:MyApp.Client.ViewModels.Menu"
         xmlns:webControls="clr-namespace:System.Web.UI.WebControls;assembly=System.Web"
         mc:Ignorable="d" 

         d:DesignHeight="100" d:DesignWidth="1200">
<Grid>

    <ListView Background="Transparent" 
              BorderBrush="Black" 
              BorderThickness="2"   
              x:Name="MenuList" 
              VerticalAlignment="Stretch" 
              HorizontalAlignment="Stretch" 
              ItemsSource="{Binding Path=MenuItems}" 
              SelectionChanged="MenuList_OnSelectionChanged" 
              SelectedItem="{Binding Path=SelectedItem}" 
              SelectionMode="Single" >
        <ListView.ItemsPanel>
            <ItemsPanelTemplate x:Name="MenuPanelTemplate">
                <UniformGrid IsItemsHost="True" x:Name="MenuPanel">
                </UniformGrid>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>

        <ListView.Resources>
            <Style TargetType="ListViewItem" x:Name="MenuItemStyle">
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True">
                        <Setter Property="Background" Value="DarkSeaGreen" />
                        <Setter Property="Template" Value="{Binding SelectedTemplate}" />
                    </Trigger>                       
                </Style.Triggers>
            </Style>
        </ListView.Resources>
     </ListView>
</Grid>

SelectedTemplate是用户控件的依赖属性:

 public static readonly DependencyProperty SelectedTemplateProperty = DependencyProperty.Register(
        "SelectedTemplate", typeof(ControlTemplate), typeof(MenuContainer), new PropertyMetadata(default(ControlTemplate)));

 public ControlTemplate SelectedTemplate {
     get { return (ControlTemplate)GetValue(SelectedTemplateProperty); }
     set { SetValue(SelectedTemplateProperty, value); }
 }

我添加了一个SelectedTemplateChanged方法来控制模板更改时发生的情况:

在构造函数中:

 DependencyPropertyDescriptor.FromProperty(SelectedTemplateProperty, GetType()).AddValueChanged(this, SelectedTemplateChanged);
然后执行。我知道我们在用户控件的绑定中设置模板时输入此代码,但我无法弄清楚如何将模板应用于listview选择项。

private void SelectedTemplateChanged(object sender, EventArgs e)
{
   //How to access and set the template of the ListView (named MenuList in the xaml) selected item from 
   //code behind?

   //pseudocode follows:
   //Find the resources of MenuList
   //find trigger for isSelected
   //apply the SelectedTemplate on a setter of the IsSelected trigger...
}

以下是在主窗口中使用用户控件。

我将不同的ControlTemplates从资源文件(见下文)绑定到每个menu1:MenuContainer控件的SelectedTemplate

<Window x:Class="MyApp.Client.Views.MainWindow"
    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:views="clr-namespace:MyApp.Client.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:viewModels="clr-namespace:MyApp.Client.ViewModels"
    xmlns:menu1="clr-namespace:MyApp.Client.Views.Menu"
    xmlns:menu="clr-namespace:MyApp.Client.ViewModels.Menu"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance viewModels:MyAppViewModel, IsDesignTimeCreatable=True}"
    Title="MainWindow" Height="800" Width="1200">
<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="{Binding MenuViewModel.TopMenuHeigth}" MaxHeight="100" />
        <RowDefinition Height="600*"/>
        <RowDefinition Height="{Binding MenuViewModel.BottomMenuHeight}" MaxHeight="70"/>
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="800*"/>
        <ColumnDefinition Width="{Binding MenuViewModel.RightMenuWidth}" MaxWidth="160"/>
    </Grid.ColumnDefinitions>

    <Grid Name="TopMenuGrid" Row="0" Column="0" ColumnSpan="2">
        <menu1:MenuContainer x:Name="TopMenu" 
                       MenuOrientation="Horizontal" 
                       SelectedItem="{Binding MenuViewModel.SelectedHeaderMenuItem, Mode=TwoWay}"  
                       MenuItems="{Binding MenuViewModel.HeaderMenuItems}"
                       SelectedTemplate="{StaticResource HeaderMenuSelectedItemTemplate}">
        </menu1:MenuContainer>
    </Grid>
    <Grid Name="RightMenuGrid" Column="1" Row="1">
        <menu1:MenuContainer x:Name="RightMenu" 
                       MenuOrientation="Vertical" 
                       SelectedItem="{Binding MenuViewModel.SelectedRightItem, Mode=TwoWay}" 
                       MenuItems="{Binding MenuViewModel.RightMenuItems}"
                       SelectedTemplate="{StaticResource RightMenuSelectedItemTemplate}">
        </menu1:MenuContainer>

    </Grid>
    <Grid Row="1" Name="ContentGrid" Column="0" ColumnSpan="1">
        <TabControl Name="ContentTabControl" 
                SelectedIndex="{Binding MenuViewModel.SelectedPage}">
                <TabItem Header="Home Page">
                <TextBlock>Here comes the home page</TextBlock>
            </TabItem>
        </TabControl>
    </Grid>
    <Grid Name="DetailMenuGrid" Grid.Row="2" ColumnSpan="2" Column="0">
        <menu1:MenuContainer Background="Aquamarine"
                        x:Name="BottomMenu" MenuOrientation="Horizontal" 
                        SelectedItem="{Binding MenuViewModel.SelectedBottomItemItem, Mode=TwoWay}"  
                        MenuItems="{Binding MenuViewModel.BottomMenuItems}"
                        SelectedTemplate="{StaticResource BottomMenuSelectedItemTemplate}">
    </menu1:MenuContainer>                    
    </Grid>
</Grid>

风格:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:local="clr-namespace:MyApp.Client.Resources"
                xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
                xmlns:helpers="clr-namespace:MyApp.CommonGui.Helpers;assembly=MyApp.CommonGui">
<ControlTemplate x:Key="BottomMenuSelectedItemTemplate" TargetType="ListViewItem">
    <Border SnapsToDevicePixels="true" 
                BorderBrush="{TemplateBinding BorderBrush}" 
                BorderThickness="{TemplateBinding BorderThickness}" 
                Background="{TemplateBinding Background}"
                CornerRadius="5" x:Name="border"
                >
        <StackPanel>
            <Popup PlacementTarget="{Binding ElementName=TextBlockContent}" 
                       Child="{Binding SelectedControl}" 
                       Width="{TemplateBinding Width}" 
                       Placement="Top" VerticalOffset="-2" AllowsTransparency="True" 
                       IsOpen="{Binding IsActive, Mode=TwoWay}">
                <i:Interaction.Behaviors>
                    <helpers:AutoRepositionPopupBehavior/>
                </i:Interaction.Behaviors>
            </Popup>
            <ContentControl 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                Margin="0,0,0,0" 
                VerticalAlignment="Center">

                <Grid VerticalAlignment="Center">
                    <ToggleButton  
                        Height="64"
                        BorderThickness="0"
                        Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"
                        VerticalAlignment="Bottom" 
                        x:Name="TextBlockContent"  
                        Content="{Binding Name}"
                        VerticalContentAlignment="Center"
                        IsChecked="{Binding IsActive}"
                        ></ToggleButton>
                  </Grid>
            </ContentControl>
        </StackPanel>
    </Border>
</ControlTemplate>
<ControlTemplate x:Key="HeaderMenuSelectedItemTemplate" TargetType="ListViewItem">
    <Border SnapsToDevicePixels="true" 
                BorderBrush="{TemplateBinding BorderBrush}" 
                BorderThickness="{TemplateBinding BorderThickness}" 
                Background="{TemplateBinding Background}"
                CornerRadius="5" x:Name="border"
                >
        <StackPanel>
            <ContentControl 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                Margin="0,0,0,0" 
                VerticalAlignment="Center">
                <Grid VerticalAlignment="Center">
                    <Grid>
                        <TextBlock x:Name="TextBlockContent" Text="{Binding 
                    </Grid>
                </Grid>
            </ContentControl>
        </StackPanel>
    </Border>
</ControlTemplate>
<ControlTemplate x:Key="RightMenuSelectedItemTemplate" TargetType="ListViewItem">
    <Border SnapsToDevicePixels="true" 
                BorderBrush="{TemplateBinding BorderBrush}" 
                BorderThickness="{TemplateBinding BorderThickness}" 
                Background="{TemplateBinding Background}"
                CornerRadius="5" x:Name="border"
                >
        <StackPanel>
            <ContentControl 
                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                Margin="0,0,0,0" 
                VerticalAlignment="Center">
                <Grid VerticalAlignment="Center">
                    <ToggleButton  
                       Height="64"
                       BorderThickness="0"
                       Background="{Binding 
                               RelativeSource={RelativeSource TemplatedParent}, 
                               Path=Background}"
                        VerticalAlignment="Bottom" 
                        x:Name="TextBlockContent"  
                        Content="{Binding Name}"
                        VerticalContentAlignment="Center"
                        IsChecked="{Binding IsActive}">
                    </ToggleButton>
                </Grid>
            </ContentControl>
        </StackPanel>
    </Border>
</ControlTemplate>

我希望我能在这里清楚地看到我的问题: 我需要知道MenuContainer用户控件后面的代码的SelectedTemplateChanged方法是什么,以便设置ListView的所选项的样式。

但我相信这可以更优雅地完成。我对“高级”的xaml和模板样式很新,所以我有点陷入困境。我已经在论坛上搜索了几个小时试图寻找解决方案,但我还没有成功。

如果没有SelectedTemplateChanged背后的代码,是否有可能实现这一目标?

任何帮助表示赞赏,既可以直接解决我的问题,也可以替代解决方案。

1 个答案:

答案 0 :(得分:0)

尝试使用ContentProvider绑定到SelectedTemplate的{​​{1}}属性:

UserControl