如何在xaml中通过DataTrigger切换Control?

How to switch Control by DataTrigger in the xaml?

有一个window由两个控件组成。 一个是TreeView,一个是ListBox。

代码如下图

<Grid Grid.Row="1">
    <TreeView x:Name="treeView" BorderThickness="0" Visibility="Visible"
                ItemsSource="{Binding TotalCPUs}">

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <mvvm:EventToCommand Command="{Binding CPUSelectedCommand}"
                                 PassEventArgsToCommand="True"
                                 EventArgsConverter="{localConverters:SelectedItemConverter}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TreeView>

    <ListBox x:Name="listBox" BorderThickness="0" Visibility="Collapsed"/>
</Grid>

现在,每当 ViewModel 属性 发生变化时,我想更改控件的可见性 属性 的值。 (FilterMode True = ListBox Visible, FilterMode False = TreeView = Visible)

为此,我修改了我的 XAML 代码如下。

<Grid Grid.Row="1">
    <TreeView x:Name="treeView" BorderThickness="0" Visibility="Visible"
                ItemsSource="{Binding TotalCPUs}">
        <TreeView.Style>
            <Style TargetType="{x:Type TreeView}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding FilterMode}" Value="true">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>                                
            </Style>
        </TreeView.Style>

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <mvvm:EventToCommand Command="{Binding CPUSelectedCommand}"
                                 PassEventArgsToCommand="True"
                                 EventArgsConverter="{localConverters:SelectedItemConverter}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TreeView>

    <ListBox x:Name="listBox" BorderThickness="0" Visibility="Collapsed">
        <ListBox.Style>
            <Style TargetType="{x:Type ListBox}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding FilterMode}" Value="true">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListBox.Style>
    </ListBox>
</Grid>

下面是 ViewModel 代码。

public class NewProjectViewModel : DialogViewModel
{
    private Generator projectGenerator = new Generator();

    public ObservableCollection<ClassHierarchyData> TotalCPUs { get; private set; } = new ObservableCollection<ClassHierarchyData>();
    public ObservableCollection<DetailType> FilterCPUs { get; private set; } = new ObservableCollection<DetailType>();

    private bool filterMode;
    public bool FilterMode
    {
        get => filterMode;
        set
        {
            if (this.filterMode == value) return;
            this.filterMode = value;
            this.RaisePropertyChanged("FilterMode");
        }
    }

    private string cpuSearch;
    public string CPUSearch
    {
        get => this.cpuSearch;
        set
        {
            if (this.cpuSearch == value) return;
            this.cpuSearch = value;
            this.FilterCPUs.Add(new DetailType(typeof(Target), "abc"));
        }
    }

    private Type selectedTerminalItem;
    public Type SelectedTerminalItem
    {
        get => this.selectedTerminalItem;
        private set
        {
            if (this.selectedTerminalItem == value) return;
            this.selectedTerminalItem = value;

            this.RaisePropertyChanged("SelectedTerminalItem");
        }
    }

    private Type selectedItem;
    public Type SelectedItem
    {
        get => selectedItem;
        set
        {
            if (this.selectedItem == value) return;
            this.selectedItem = value;
            this.RaisePropertyChanged("SelectedItem");

            CreateCommand.RaiseCanExecuteChanged();
        }
    }

    private string solutionName = string.Empty;
    public string SolutionName
    {
        get => this.solutionName;
        set
        {
            if (this.solutionName == value) return;

            this.solutionName = value;
            this.RaisePropertyChanged("SolutionName");
            this.RaisePropertyChanged("SolutionFullPath");

            CreateCommand.RaiseCanExecuteChanged();
        }
    }

    private string solutionPath = string.Empty;
    public string SolutionPath
    {
        get => this.solutionPath;
        set
        {
            if (this.solutionPath == value) return;

            this.solutionPath = value;
            if(this.SolutionPath.Length > 0)
            {
                if (this.solutionPath.Last() != '\')
                    this.solutionPath += "\";
            }
            this.RaisePropertyChanged("SolutionPath");
            this.RaisePropertyChanged("SolutionFullPath");

            CreateCommand.RaiseCanExecuteChanged();
        }
    }

    public bool CreateSolutionFolder { get; set; }
    public string SolutionFullPath { get => this.SolutionPath + this.solutionName; }

    private RelayCommand searchCommand;
    public RelayCommand SearchCommand
    {
        get
        {
            if (this.searchCommand == null) this.searchCommand = new RelayCommand(this.OnSearch);

            return this.searchCommand;
        }
    }
    private void OnSearch()
    {
        CommonOpenFileDialog selectFolderDialog = new CommonOpenFileDialog();

        selectFolderDialog.InitialDirectory = "C:\Users";
        selectFolderDialog.IsFolderPicker = true;
        if (selectFolderDialog.ShowDialog() == CommonFileDialogResult.Ok)
        {
            this.SolutionPath = selectFolderDialog.FileName + "\";
        }
    }

    private RelayCommand<Action> _createCommand;
    public RelayCommand<Action> CreateCommand
    {
        get
        {
            if (this._createCommand == null)
                this._createCommand = new RelayCommand<Action>(this.OnCreate, this.CanExecuteCreate);

            return this._createCommand;
        }
    }
    private void OnCreate(Action action)
    {
        projectGenerator.GenerateSolution(this.SolutionPath, this.SolutionName, this.CreateSolutionFolder);
        action?.Invoke();
    }

    private bool CanExecuteCreate(Action action)
    {
        if (this.SelectedTerminalItem == null) return false;
        if (string.IsNullOrEmpty(this.solutionPath)) return false;
        if (string.IsNullOrEmpty(this.solutionName)) return false;

        return true;
    }

    private RelayCommand<ClassHierarchyData> cpuSelectedCommand;
    public RelayCommand<ClassHierarchyData> CPUSelectedCommand
    {
        get
        {
            if (this.cpuSelectedCommand == null)
                this.cpuSelectedCommand = new RelayCommand<ClassHierarchyData>(OnCPUSelected);

            return this.cpuSelectedCommand;
        }
    }

    private void OnCPUSelected(ClassHierarchyData selected)
    {
        this.SelectedItem = selected.Data;
        this.SelectedTerminalItem = (selected.Items.Count == 0) ? selected.Data : null;
    }


    private RelayCommand<string> navigateCommand;
    public RelayCommand<string> NavigateCommand
    {
        get
        {
            if (this.navigateCommand == null)
                this.navigateCommand = new RelayCommand<string>((uri) =>
                {
                    Process.Start(new ProcessStartInfo(uri));
                });

            return navigateCommand;
        }
    }

    public NewProjectViewModel()
    {
        ClassHierarchyGenerator classHierarchyGenerator = new ClassHierarchyGenerator();

        this.TotalCPUs.Add(classHierarchyGenerator.ToHierarchyData(typeof(Target)));
        this.FilterCPUs.CollectionChanged += FilterCPUs_CollectionChanged;
    }

    private void FilterCPUs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        this.FilterMode = (this.FilterCPUs.Count > 0) ? true : false;
    }
}


public class DetailType
{
    public Type Type { get; }
    public string Path { get; }

    public DetailType(Type type, string path)
    {
        Type = type;
        Path = path;
    }
}

如果用户向TextBox输入数据进行过滤,那么CPUSearch的值就会改变。

如果更改了 CPUSearch 的值,则将测试值添加到 FilterCPUs 中。 (注意 CPUSearch 属性) 将值添加到 FilterCPU 时,调用 FilterCPUs_CollectionChanged 并更改 FilterMode 的值。

但是上面的代码虽然改变了 FilterMode 的值,但并没有起作用。 (除了与 FilterMode 相关的功能外,效果很好)

为什么 Control 没有切换?

感谢阅读。

我参考WPF Showing / Hiding a control with triggers解决了这个问题

我的 XAML 代码更新后运行良好。

感谢您的关注。

<Grid Grid.Row="1">
    <TreeView x:Name="treeView" BorderThickness="0"
                ItemsSource="{Binding TotalCPUs}">
        <TreeView.Style>
            <Style TargetType="{x:Type TreeView}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding FilterMode}" Value="true">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding FilterMode}" Value="false">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TreeView.Style>

        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectedItemChanged">
                <mvvm:EventToCommand Command="{Binding CPUSelectedCommand}"
                                 PassEventArgsToCommand="True"
                                 EventArgsConverter="{localConverters:SelectedItemConverter}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TreeView>

    <ListBox x:Name="listBox" BorderThickness="0"
                    ItemsSource="{Binding FilterCPUs}"
                    SelectedItem="{Binding SelectedItem}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock FontSize="8" Text="{Binding Path=Path}" Margin="0 0 0 3"/>
                    <TextBlock Text="{Binding Type.Name}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>

        <ListBox.Style>
            <Style TargetType="{x:Type ListBox}">
                <Setter Property="Background" Value="{DynamicResource CommonEnableBackgroundBrush}"/>
                <Setter Property="BorderBrush" Value="{DynamicResource CommonEnableBorderBrush}"/>
                <Setter Property="Foreground" Value="{DynamicResource CommonEnableTextBrush}"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding FilterMode}" Value="true">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                    <DataTrigger Binding="{Binding FilterMode}" Value="false">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListBox.Style>
    </ListBox>
</Grid>