![](/img/trans.png)
[英]Can I set my user control datacontext in xaml via a dependency property bound to a property on the parent control in wpf?
[英]WPF: User Control access Style via Dependency Property
我为带有图像的按钮创建了样式:
<Style x:Key="StyleButtonBase" TargetType="Button">
<Style.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Sizes/Sizes.xaml" />
<ResourceDictionary Source="../Colors/Brushes.xaml" />
<ResourceDictionary Source="../Fonts/Fonts.xaml" />
<ResourceDictionary Source="../Images/Images.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BrushButtonActive}" />
<Setter Property="Foreground" Value="{StaticResource BrushForegroundLight}" />
<Setter Property="FontFamily" Value="{StaticResource FontFamilyDefault}" />
<Setter Property="FontSize" Value="{StaticResource DoubleFontSizeStandard}" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource BrushBorder}"
BorderThickness="{StaticResource ThicknessBorder}"
CornerRadius="{StaticResource CornerRadius}">
<Image Source="{StaticResource IconIcon}" Stretch="None" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{StaticResource BrushButtonPressed}" />
</Trigger>
</Style.Triggers>
</Style>
现在,我想创建一个用户控件,该控件仅包含具有这种样式的按钮和用于设置按钮图像的Dependency属性。 我的用户控件的XAML部分如下所示:
<UserControl
x:Class="HH.HMI.ToolSuite.ResourceLib.Controls.ButtonSmall">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Styles/Buttons.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Button Style="{StaticResource StyleButtonBase}" Width="{StaticResource DoubleWidthButtonSmall}" Height="{StaticResource DoubleHeightControls}">
</Button></UserControl>
我的用户控件背后的代码如下所示:
public partial class ButtonSmall : UserControl, INotifyPropertyChanged
{
public ButtonSmall()
{
InitializeComponent();
}
public static readonly DependencyProperty ButtonImageProperty
= DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(TextOutput), new PropertyMetadata(null, OnButtonImagePropertyChanged));
private static void OnButtonImagePropertyChanged(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
ButtonSmall temp = dependencyObject as ButtonSmall;
temp.OnPropertyChanged("ButtonImage");
temp.OnButtonImagePropertyChanged(e);
}
private void OnButtonImagePropertyChanged(DependencyPropertyChangedEventArgs e)
{
ButtonSmallImage.Source = ButtonImageSource;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public ImageSource ButtonImageSource
{
get { return (ImageSource)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
}
在其他用户控件中,我通常访问用户控件本身中的元素,例如:
xamlname.text = text
现在,我的用户控件的xaml代码中没有命名元素。 相反,我有样式中的命名元素,我在用户控件中引用了该元素。 如何通过我的代码访问此代码?
如果我是我,我将Button子类化,然后创建一个新类(只是一个.cs文件),如下所示:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MyProject
{
public class IconButton : Button
{
public static readonly DependencyProperty ButtonImageProperty = DependencyProperty.Register("ButtonImage", typeof(ImageSource), typeof(IconButton),
new FrameworkPropertyMetadata(new BitmapImage(), FrameworkPropertyMetadataOptions.AffectsRender));
public ImageSource ButtonImage
{
get { return (ImageSource)GetValue(ButtonImageProperty); }
set { SetValue(ButtonImageProperty, value); }
}
}
}
这意味着您现在可以引用该属性。 否则,由于您的按钮只是一个常规按钮(仅具有常规按钮的属性;没有图像),因此您的样式不知道希望按钮具有的新图像属性。 不要忘记更新样式的TargetType使其指向IconButton。
如果将样式放在用户控件的资源部分中,则可以这样设置按钮样式:
<UserControl x:Class="MyProject.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myclass="clr-namespace:MyProject">
<UserControl.Resources>
<!-- your style here -->
</UserControl.Resources>
<myclass:IconButton Style="{StaticResource StyleButtonBase}/>
</UserControl>
(必须替换xmlns'myclass'来引用您的自定义按钮所在的名称空间!)
另外,如果从样式中删除x:Key属性,它将应用于作用域中的所有按钮,这意味着您可以省略显式设置。 如果将其放在共享的ResourceDictionary中(例如,如果要构建自定义控件的库),这可能会很方便(如果这样做,则需要在App.xaml.cs文件中组合此资源字典)。 如果最终这样做了,而您发现UserControl除了包装IconButton并没有任何特殊功能,您当然可以完全省略它,而直接在其他控件中使用IconButtons。 您的样式声明了IconButton的外观,并且IconButton类确保您的样式在运行时寻找它们时期望的资源(您的图像)存在,因此,只要您的样式在范围内,就可以使用。
如果在App.xaml的Application.Resources
或合并到App.xaml的Application.Resources
的资源字典中定义了样式,则可以通过StaticResource
在用户控件中引用它。 如果它在另一本资源字典中,则必须将其合并到UserControl.Resources
。
或者,也可以按照TernaryTopiary的建议将其直接放在UserControl.Resources
中,如果在其他地方不需要它。
至于图像源属性,您可以按照Ternary的建议编写Button子类,也可以编写附加的属性(请参见下文)。 在XAML中,当您自定义控件时,首先尝试使用常规属性来完成此操作; 然后您尝试重新设置样式。 然后升级为替换控件模板,而这样做并不能完全解决问题,您需要考虑附加的属性/行为。 只有在所有其他方法都无法解决的情况下,您才可以使用子类化。 您应该知道如何做,但是您还应该学习其他做事方式。
在这种情况下,有一种快速而又肮脏的方法与正确的XAML处理方法一致:按钮的Content
属性不再使用,其声明的类型为Object
,因此我们可以使用它。 由于我们使用绑定来传递图像源,因此您可以摆脱ButtonImageSource
上的那个PropertyChanged
处理程序。
<UserControl
...>
<!-- ... -->
<Button
Content="{Binding ButtonImageSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
Style="{StaticResource StyleButtonBase}"
Width="{StaticResource DoubleWidthButtonSmall}"
Height="{StaticResource DoubleHeightControls}"
/>
并在StyleButtonBase
中的控件模板中进行以下更改:
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{StaticResource BrushBorder}"
BorderThickness="{StaticResource ThicknessBorder}"
CornerRadius="{StaticResource CornerRadius}"
>
<!-- Now we'll find the image source in the button's Content --->
<Image
Source="{TemplateBinding Content}"
Stretch="None"
/>
</Border>
</ControlTemplate>
使用Content
对此是WPF的一种非常轻微的滥用:您不应该真正改变属性的用途。 在WPF中, Content
被普遍理解为表示“任何任意内容”,而不是“任何ImageSource
”。
因此,使用附加属性稍微有些“正确”,而且工作量也不大。 这就是它的外观。
我们将在一个单独的静态类中定义附加属性,因为除了ButtonImageSource
,我想不出它的好名字,您已经在SmallButton
使用了SmallButton
:
public static class ButtonHelper
{
#region ButtonHelper.ButtonImageSource Attached Property
public static ImageSource GetButtonImageSource(Button obj)
{
return (ImageSource)obj.GetValue(ButtonImageSourceProperty);
}
public static void SetButtonImageSource(Button obj, ImageSource value)
{
obj.SetValue(ButtonImageSourceProperty, value);
}
public static readonly DependencyProperty ButtonImageSourceProperty =
DependencyProperty.RegisterAttached("ButtonImageSource", typeof(ImageSource), typeof(ButtonHelper),
new PropertyMetadata(null));
#endregion ButtonHelper.ButtonImageSource Attached Property
}
在XAML中,用户控件使用此附加属性而不是Content
:
<Button
Style="{StaticResource StyleButtonBase}"
local:ButtonHelper.ButtonImageSource="{Binding ButtonImageSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
控件模板也这样做:
<ControlTemplate TargetType="Button">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="4"
>
<Image
Source="{TemplateBinding local:ButtonHelper.ButtonImageSource}"
Stretch="None"
/>
</Border>
</ControlTemplate>
附加的属性所做的就是为我们提供一个不名为Content
的属性,该属性的类型很强,为ImageSource
,我们可以使用该属性来传递该图像源。
另一件事:可能是当您简化问题代码时出现的一个错误,但是您正在将typeof(TextOutput)
传递给DependencyProperty.Register()
,您应该在其中传递typeof(ButtonSmall)
。 更重要的是,对于一个应具有的属性,您有两个名称: ButtonImage
和ButtonImageSource
。
public static readonly DependencyProperty ButtonImageSourceProperty
= DependencyProperty.Register(
"ButtonImageSource",
typeof(ImageSource),
// Should be ButtonSmall, not TextOutput
typeof(ButtonSmall),
new PropertyMetadata(null));
public ImageSource ButtonImageSource
{
get { return (ImageSource)GetValue(ButtonImageSourceProperty); }
set { SetValue(ButtonImageSourceProperty, value); }
}
顺便说一句,在您的Style中,最好将TemplateBinding
用于BorderBrush
和BorderThickness
,并在Style setter中设置默认值,就像使用Background
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.