简体   繁体   中英

WPF: Setting ListView View via DataTrigger

I have a list view and 2 resources for display the list's view: BooksGridView & ImageDetailView.

The ViewModel has a string property named ViewMode, which contains the name of the view i currently want to display. (It is changed from another control, using toolbars)

I am trying to change the selected view by using DataTrigger , but I cant seem to get the View property to change.

When i set the View resource directly, the correct view is displayed. I also added background changes to make sure the data trigger is activated, and the background did change.

So I'm obviously missing something here...

<UserControl x:Class="eLibrary.View.FilteredBooksView"
             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:Converters="clr-namespace:eLibrary.Converters"
             xmlns:Controls="clr-namespace:eLibrary.Controls"
             xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
             xmlns:local="clr-namespace:eLibrary"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <Converters:CoverImageConverter x:Key="CoverImageConverter"/>
        <BooleanToVisibilityConverter x:Key="BoolToVisConverter" />

        <GridView x:Key="BooksGridView">
            ...
        </GridView>

        <Controls:TileView x:Key="ImageDetailView">
            ...
        </Controls:TileView>

        <CollectionViewSource x:Key="sortedBooks" Source="{Binding Books}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="Title" Direction="Ascending"/>
            </CollectionViewSource.SortDescriptions>
        </CollectionViewSource>

        <Style TargetType="{x:Type ListView}">
            <Style.Triggers>

                <DataTrigger Binding="{Binding Path=ViewMode}" Value="BooksGridView">
                    <Setter Property="View" Value="{StaticResource BooksGridView}"/>
                    <Setter Property="Background" Value="Red"/>
                </DataTrigger>

                <DataTrigger Binding="{Binding Path=ViewMode}" Value="ImageDetailView">
                    <Setter Property="View" Value="{StaticResource ImageDetailView}" />
                    <Setter Property="Background" Value="Blue"/>
                </DataTrigger>

            </Style.Triggers>
        </Style>

    </UserControl.Resources>

    <ListView
        VerticalAlignment="Stretch"
        Name="BooksListView"
        View="{StaticResource BooksGridView}"
        SelectionMode="Extended"
        ItemsSource="{Binding Source={StaticResource sortedBooks}}">
        <ListView.Resources>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay}" />
                <Setter Property="Visibility" Value="{Binding Path=ShouldDisplay, Converter={StaticResource BoolToVisConverter} }" />
            </Style>
        </ListView.Resources>
    </ListView>

</UserControl>

Thanks

Based off the sample on MSDN, the following works at changing the view based on a change in the ViewModel. The only difference I can see with your code is the use of DynamicResource :

<Window x:Class="SDKSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Custom View" 
    xmlns:l="clr-namespace:SDKSample" 
    Width="400" Height="500"
    SourceInitialized="Window_SourceInitialized">

    <Window.Resources>

      <DataTemplate x:Key="centralTile">
        <StackPanel Height="100" Width="90">
          <Grid Width="70" Height="70" HorizontalAlignment="Center">
            <Image Source="{Binding XPath=@Image}" Margin="6,6,6,9"/>
          </Grid>
          <TextBlock Text="{Binding XPath=@Name}" FontSize="13" 
                     HorizontalAlignment="Center" Margin="0,0,0,1" />
          <TextBlock Text="{Binding XPath=@Type}" FontSize="9" 
                     HorizontalAlignment="Center" Margin="0,0,0,1" />
        </StackPanel>
      </DataTemplate>

      <DataTemplate x:Key="iconTemplate">
        <DockPanel Height="33" Width="150">
          <Image Source="{Binding XPath=@Image}" Margin="2"/>
          <TextBlock DockPanel.Dock="Top" Text="{Binding XPath=@Name}" 
                     FontSize="13" HorizontalAlignment="Left" 
                     Margin="0,0,0,1" />
          <TextBlock Text="{Binding XPath=@Type}" FontSize="9" 
                     HorizontalAlignment="Left" Margin="0,0,0,1" />
        </DockPanel>
      </DataTemplate>

      <DataTemplate x:Key="checkbox">
        <CheckBox IsChecked="{Binding IsSelected, RelativeSource= {RelativeSource AncestorType=ListViewItem}}" 
                  Margin="0,1,1,1" >
        </CheckBox>
      </DataTemplate>

      <XmlDataProvider x:Key="myXmlDataBase" XPath="/myXmlData">
        <x:XData>
          <myXmlData xmlns="">
            <Item Name = "Fish" Type="fish" Image="images\fish.png"/>
            <Item Name = "Dog" Type="animal" Image="images\dog.png"/>
            <Item Name = "Flower" Type="plant" Image="images\flower.jpg"/>
            <Item Name = "Cat" Type="animal" Image="images\cat.png"/>
          </myXmlData>
        </x:XData>
      </XmlDataProvider>

      <DataTemplate x:Key="DisplayImage">
        <StackPanel Width="50">
          <Image Source="{Binding XPath=@Image}"/>
        </StackPanel>
      </DataTemplate>

      <GridView x:Key="gridView">
        <GridViewColumn CellTemplate="{StaticResource checkbox}"/>
        <GridViewColumn Header="Name" DisplayMemberBinding="{Binding XPath=@Name}"/>
        <GridViewColumn Header="Type" DisplayMemberBinding="{Binding XPath=@Type}"/>
        <GridViewColumn Header="Image" CellTemplate="{StaticResource DisplayImage}"/>
      </GridView>
      <l:PlainView x:Key="tileView" ItemTemplate="{StaticResource centralTile}" ItemWidth="100"/>
      <l:PlainView x:Key="iconView" ItemTemplate="{StaticResource iconTemplate}"  ItemWidth="150"/>

    <Style TargetType="{x:Type ListView}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=ViewName}" Value="iconView">
                <Setter Property="View" Value="{DynamicResource iconView}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=ViewName}" Value="tileView">
                <Setter Property="View" Value="{DynamicResource tileView}" />
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=ViewName}" Value="gridView">
                <Setter Property="View" Value="{DynamicResource gridView}" />
            </DataTrigger>
        </Style.Triggers>
    </Style>

    </Window.Resources>

  <StackPanel>
    <ListView Name="lv"
              ItemsSource="{Binding Source={StaticResource myXmlDataBase}, XPath=Item}" 
              FontSize="12" 
              Background="LightBlue" >
      <ListView.ContextMenu>
        <ContextMenu>
          <MenuItem Header="gridView" Click="SwitchViewMenu"/>
          <MenuItem Header="iconView" Click="SwitchViewMenu"/>
          <MenuItem Header="tileView" Click="SwitchViewMenu"/>
        </ContextMenu>
      </ListView.ContextMenu>


    </ListView>
    <TextBlock FontSize="16" Foreground="Blue">
      CurrentView: <TextBlock Name="currentView" Text="{Binding Path=ViewName}"/>
    </TextBlock>
    <TextBlock>
      Right-click in the content window to change the view.
      </TextBlock>

  </StackPanel>
 </Window>

Code behind file:

using System;
using System.Windows;
using System.Windows.Controls;

namespace SDKSample
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        public MainViewModel ViewModel
        {
            get { return this.DataContext as MainViewModel; }
        }

        void SwitchViewMenu(object sender, RoutedEventArgs args)
        {
            MenuItem mi = (MenuItem)sender;
            ViewModel.ViewName = mi.Header.ToString();
        }

        private void Window_SourceInitialized(object sender, EventArgs e)
        {
            ViewModel.ViewName = "gridView";
        }
    }
}

And finally the ViewModel class:

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;

namespace SDKSample
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public string ViewName
        {
            get { return viewName; }
            set
            {
                if (viewName == value)
                    return;

                viewName = value;
                NotifyPropertyChanged("ViewName");
            }
        }
        private string viewName;

        public event PropertyChangedEventHandler PropertyChanged;

        void NotifyPropertyChanged(string name)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
}

I cannot see any obvious issue with your code you provided. I would usually suggest checking that the DataTrigger is being trigger, however you have already tested that with the Background property.

Looking at sample on MSDN ( link ) the only difference in implementation is that, in the sample, switch the ListView.View is changed in code.

BookListView.View = this.FindResource("BooksGridView") as ViewBase;

...

Hmm, perhaps the view resource may not be able to found and it is failing.

...

All I can suggest is look at the sample. Sorry couldn't be much more help.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM