WPF 附加属性
WPF AttachedProperty
我创建了一个简单的测试项目,它创建了一个填充有复选框的列表框。我有一些代码隐藏,只要选中复选框,ListBox 上方的 TextBox 就会显示所有选定项目的文本。一切正常,但我现在正在尝试创建一个允许包含前缀的 Attached 属性(同样在后面的代码中)。最终我希望将其转换为 MVVM 标准,但因为我对 WPF 比较陌生,对 MVVM 也很陌生,所以我喜欢从一个工作示例项目开始工作,并逐渐将每个部分转换为一个 MVVM 项目,因为我发现这有助于我更好地理解什么正在进行。
我的代码如下:
xaml:
<Window x:Class="CheckBoxList.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:CheckBoxList"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="400">
<Window.Resources>
<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<ContentPresenter></ContentPresenter>
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="ListBoxTextBox"/>
<ListBox Grid.Row="1" Style="{StaticResource CheckBoxListStyle}" Name="lstProducts" local:MainWindow.PrefixMark=">"
DisplayMemberPath="ModelName">
</ListBox>
<Button Grid.Row="2" Margin="0,5,0,0" Click="cmdGetSelectedItems">Get Selected Items</Button>
<CheckBox Grid.Row="3" Content="Test" Margin="5"/>
</Grid>
xaml.cs
public static string GetPrefixMark(DependencyObject obj)
{
string t = (string)obj.GetValue(PrefixMarkProperty);
return (string)obj.GetValue(PrefixMarkProperty);
}
public static void SetPrefixMark(DependencyObject obj, string value)
{
obj.SetValue(PrefixMarkProperty, value);
string t = (string)obj.GetValue(PrefixMarkProperty);
}
// Using a DependencyProperty as the backing store for PrefixMark. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PrefixMarkProperty =
DependencyProperty.RegisterAttached("SetPrefixMark", typeof(string), typeof(MainWindow), new PropertyMetadata("('"));
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement element)
{
AddText(element);
}
}
private void AddText(FrameworkElement e)
{
StringBuilder sB = new StringBuilder("");
foreach (Product product in lstProducts.SelectedItems)
{
sB.Append(product.ModelName).Append(ValueSeparator);
}
if (sB.Length > 0)
{
sB.Remove(sB.Length - (ValueSeparator).Length, (ValueSeparator).Length);
}
if (sB.Length > 0)
{
ListBoxTextBox.Text = GetPrefixMark(e) + sB.ToString();
}
else
{
ListBoxTextBox.Text = "";
}
}
当我 运行 程序时,虽然 PrefixMark 设置为加载,但每当我选中一个复选框时,它都会恢复为默认值。我只能假设这是创建了两次,但对于我来说,我无法弄清楚为什么以及如何解决这个问题?
您应该将依赖项的实际名称 属性 传递给 Register
方法以开头:
DependencyProperty.RegisterAttached("PrefixMark", ...
The SetPrefixMark is no longer being fired on startup
不应该。如果你想在启动时设置 TextBox
的 Text
属性,你应该将它绑定到源 属性 或以编程方式设置它:
public MainWindow()
{
InitializeComponent();
ListBoxTextBox.Text = GetPrefixMark(this);
...
}
我创建了一个简单的测试项目,它创建了一个填充有复选框的列表框。我有一些代码隐藏,只要选中复选框,ListBox 上方的 TextBox 就会显示所有选定项目的文本。一切正常,但我现在正在尝试创建一个允许包含前缀的 Attached 属性(同样在后面的代码中)。最终我希望将其转换为 MVVM 标准,但因为我对 WPF 比较陌生,对 MVVM 也很陌生,所以我喜欢从一个工作示例项目开始工作,并逐渐将每个部分转换为一个 MVVM 项目,因为我发现这有助于我更好地理解什么正在进行。 我的代码如下:
xaml:
<Window x:Class="CheckBoxList.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:CheckBoxList"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="400">
<Window.Resources>
<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
<Setter Property="SelectionMode" Value="Multiple"></Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="Margin" Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox
IsChecked="{Binding Path=IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">
<ContentPresenter></ContentPresenter>
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition/>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" x:Name="ListBoxTextBox"/>
<ListBox Grid.Row="1" Style="{StaticResource CheckBoxListStyle}" Name="lstProducts" local:MainWindow.PrefixMark=">"
DisplayMemberPath="ModelName">
</ListBox>
<Button Grid.Row="2" Margin="0,5,0,0" Click="cmdGetSelectedItems">Get Selected Items</Button>
<CheckBox Grid.Row="3" Content="Test" Margin="5"/>
</Grid>
xaml.cs
public static string GetPrefixMark(DependencyObject obj)
{
string t = (string)obj.GetValue(PrefixMarkProperty);
return (string)obj.GetValue(PrefixMarkProperty);
}
public static void SetPrefixMark(DependencyObject obj, string value)
{
obj.SetValue(PrefixMarkProperty, value);
string t = (string)obj.GetValue(PrefixMarkProperty);
}
// Using a DependencyProperty as the backing store for PrefixMark. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PrefixMarkProperty =
DependencyProperty.RegisterAttached("SetPrefixMark", typeof(string), typeof(MainWindow), new PropertyMetadata("('"));
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if (sender is FrameworkElement element)
{
AddText(element);
}
}
private void AddText(FrameworkElement e)
{
StringBuilder sB = new StringBuilder("");
foreach (Product product in lstProducts.SelectedItems)
{
sB.Append(product.ModelName).Append(ValueSeparator);
}
if (sB.Length > 0)
{
sB.Remove(sB.Length - (ValueSeparator).Length, (ValueSeparator).Length);
}
if (sB.Length > 0)
{
ListBoxTextBox.Text = GetPrefixMark(e) + sB.ToString();
}
else
{
ListBoxTextBox.Text = "";
}
}
当我 运行 程序时,虽然 PrefixMark 设置为加载,但每当我选中一个复选框时,它都会恢复为默认值。我只能假设这是创建了两次,但对于我来说,我无法弄清楚为什么以及如何解决这个问题?
您应该将依赖项的实际名称 属性 传递给 Register
方法以开头:
DependencyProperty.RegisterAttached("PrefixMark", ...
The SetPrefixMark is no longer being fired on startup
不应该。如果你想在启动时设置 TextBox
的 Text
属性,你应该将它绑定到源 属性 或以编程方式设置它:
public MainWindow()
{
InitializeComponent();
ListBoxTextBox.Text = GetPrefixMark(this);
...
}