【发布时间】:2020-06-18 11:36:34
【问题描述】:
我已经达到了我的心理障碍,但无法弄清楚。我确定我错过了一些简单的东西,但我被卡住了。下面的代码是查看我的问题所需的最少代码,但与我的生产代码相去甚远。
设置:
我有一个带有 DataGrid 控件的 WPF 窗口,该控件绑定到包含 资产集合 的业务对象。对于每个资产,我需要显示一个用户控件(SpecialButton),它的可见性是根据资产对象的多个属性确定的。当我单击按钮时(在我的示例中,为了简单起见,我有一个额外的按钮可以更改属性),它会更改基础资产对象的属性,该属性应该使控件隐藏。
问题
我将用户控件附加属性ControlVisibility 绑定到整个资产对象{Binding .}<local:SpecialButton x:Name="buttonOnEachRow" ControlId="{Binding Id}"
ControlVisibility="{Binding ., Converter={StaticResource MyConverter}}"/>
当我更改资产对象 PropertyA 的属性时,我希望 MyConverter 应该运行并更改可见性值,但它永远不会发生。
我的尝试
我尝试了很多我什至不记得的事情。最有希望的似乎是MultipleBinding,但我无法弄清楚如何为ControlVisibility 属性编写语法。我在DataGrid 控件上尝试了一些设置,改变了user control 的更新方式,但没有成功。
作为一种解决方法,在我的生产代码中,我创建了一个 fake 属性,该属性执行当前转换器中的逻辑并将ControlVisibility 绑定到fake 属性。这可行,但我的资产对象中有一个完全不相关的属性,只是因为我无法弄清楚绑定。
WPF 主窗口
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
namespace MultiBindingProblem
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
var sut = new BusinessObject() { Caption = "This is the parent object", Assets = new List<Asset>()};
sut.Assets.Add(new Asset() { Name = "Asset 1", Id = 1 });
sut.Assets.Add(new Asset() { Name = "Asset 2", Id = 2 });
sut.Assets.Add(new Asset() { Name = "Asset 3", Id = 3 });
this.DataContext = sut;
}
public event PropertyChangedEventHandler PropertyChanged;
private void BtnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
private void BtnChange_Click(object sender, RoutedEventArgs e)
{
((BusinessObject)this.DataContext).Assets[0].PropertyA = true;
//PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Assets"));
}
}
}
XAML
btnChange 在这里是为了简单起见。在我的生产代码中,SpecialButton 将触发我的视图模型中的属性更新
<Window x:Class="MultiBindingProblem.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MultiBindingProblem"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
<Window.Resources>
<local:TestConverter x:Key="MyConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="0.5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<TextBlock x:Name="lblMainObject" Grid.ColumnSpan="2" Grid.Row="0" FontSize="25"
Text="{Binding Caption}" />
<Button x:Name="btnCancel" Content="Cancel" IsCancel="True" IsDefault="True" Grid.Row="2" Grid.Column="1" Click="BtnCancel_Click" />
<DataGrid x:Name="dgrData" Grid.Row="1" Grid.ColumnSpan="2" AutoGenerateColumns="False" CanUserAddRows="False"
ItemsSource="{Binding Assets, NotifyOnSourceUpdated=True}" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Action button" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--
Here I bind to the whole 'Asset' object to be able to determine if the button should be
visible based on multiple properties. But changing a propety doesn't raise the converter.
I tried use multiple bindings but I was not able to figure out the syntax
-->
<local:SpecialButton x:Name="buttonOnEachRow"
ControlId="{Binding Id}"
ControlVisibility="{Binding ., Converter={StaticResource MyConverter}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<Button x:Name="btnChange" Grid.Row="2" Grid.Column="0" Content="Change visibility of the first button" Click="BtnChange_Click" />
</Grid>
</Window>
用户控件(特殊按钮)
using System.Windows;
using System.Windows.Controls;
namespace MultiBindingProblem
{
public partial class SpecialButton : UserControl
{
public SpecialButton()
{
InitializeComponent();
}
public static readonly DependencyProperty ControlIdProperty =
DependencyProperty.Register("ControlId", typeof(int),
typeof(SpecialButton));
public int ControlId
{
get { return (int)GetValue(ControlIdProperty); }
set { SetValue(ControlIdProperty, value); }
}
public static readonly DependencyProperty ControlVisibilityProperty =
DependencyProperty.Register("ControlVisibility", typeof(Visibility),
typeof(SpecialButton), new FrameworkPropertyMetadata(Visibility.Visible));
public Visibility ControlVisibility
{
get { return (Visibility)GetValue(ControlVisibilityProperty); }
set { SetValue(ControlVisibilityProperty, value); }
}
private void btnSpecialButton_Click(object sender, RoutedEventArgs e)
{
System.Windows.MessageBox.Show($"The id of the button: {((Button)sender).Tag.ToString()}");
}
}
}
XAML
<UserControl x:Class="MultiBindingProblem.SpecialButton"
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:MultiBindingProblem"
mc:Ignorable="d"
d:DesignHeight="45" d:DesignWidth="80"
x:Name="parent">
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch" DataContext="{Binding ElementName=parent}">
<Button x:Name="btnSpecialButton" Content="Click Me" Click="btnSpecialButton_Click"
Tag="{Binding ControlId}"
Visibility="{Binding ControlVisibility}" />
</StackPanel>
</Grid>
</UserControl>
TestConverter
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace MultiBindingProblem
{
public class TestConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var asset = value as Asset;
if (asset == null) return Visibility.Hidden;
return !(asset.PropertyA || asset.PropertyB) ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
问题
我可以以某种方式使用Multibinding吗?
或者
当资产对象上的单个属性发生更改时,如何使转换器运行?
【问题讨论】:
-
"当我更改资产对象 PropertyA 的属性时,我希望 MyConverter 应该运行并更改可见性" - 这种预期是错误的。 MultiBinding 到一组属性应该可以工作。
标签: c# wpf data-binding datagrid