我正在创建一个WPF自定义控件,Button
Image
和Text
。我已经为控件ImagePath
和Text
添加了两个依赖项属性,而控件模板(在Themes \ Generic.xaml中)是一个简单的堆栈面板,可以水平排列图像和文本。
Text
属性正常。但由于某些原因,当我使用TemplateBinding
到ImagePath
依赖项属性来获取其路径时,我的测试项目中的示例图像不会出现。我通过暂时将自定义控件中的TemplateBinding
替换为图像路径来测试图像,在这种情况下会出现。
我希望在这方面有更多经验的人可以看一看并告诉我为什么控制没有按预期工作。谢谢你的帮助。
我的VS 2008解决方案包含一个项目CustomControlDemo。该项目包含一个自定义控件,TaskButton.cs和一个主窗口Window1.xaml,我用它来测试控件。我的测试图像calendar.png位于项目根级别的Resources文件夹中,Generic.xaml位于Themes文件夹中,也位于项目的根级别。
以下是我的自定义控件的代码(来自TaskButton.cs):
using System.Windows;
using System.Windows.Controls;
namespace CustomControlDemo
{
public class TaskButton : RadioButton
{
#region Fields
// Dependency property backing variables
public static readonly DependencyProperty ImagePathProperty;
public static readonly DependencyProperty TextProperty;
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
static TaskButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TaskButton), new FrameworkPropertyMetadata(typeof(TaskButton)));
// Initialize ImagePath dependency properties
ImagePathProperty = DependencyProperty.Register("ImagePath", typeof(string), typeof(TaskButton), new UIPropertyMetadata(null));
TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(TaskButton), new UIPropertyMetadata(null));
}
#endregion
#region Dependency Property Wrappers
/// <summary>
/// The ImagePath dependency property.
/// </summary>
public string ImagePath
{
get { return (string)GetValue(ImagePathProperty); }
set { SetValue(ImagePathProperty, value); }
}
/// <summary>
/// The Text dependency property.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
#endregion
}
}
这是控件模板(来自Generic.xaml):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControlDemo">
<Style TargetType="{x:Type local:TaskButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TaskButton}">
<StackPanel Height="Auto" Orientation="Horizontal">
<Image Source="{TemplateBinding ImagePath}" Width="24" Height="24" Stretch="Fill"/>
<TextBlock Text="{TemplateBinding Text}" HorizontalAlignment="Left" Foreground="{DynamicResource TaskButtonTextBrush}" FontWeight="Bold" Margin="5,0,0,0" VerticalAlignment="Center" FontSize="12" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
最后,这是我用来测试控件的Window1标记:
<Window x:Class="CustomControlDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customControl="clr-namespace:CustomControlDemo"
Title="Window1" Height="300" Width="300">
<Grid>
<customControl:TaskButton ImagePath="Resources\calendar.png" Text="Calendar" />
</Grid>
</Window>
为什么图像路径不起作用的任何想法?再次感谢。
答案 0 :(得分:10)
我将把cwap的答案作为公认的答案,因为它在技术上是正确的。但事实证明,有一种更简单的方法可以解决这个问题。
TemplateBindings不是一流的Binding对象。它们设计为轻量级,因此它们是单向的,并且它们缺少其他Binding对象的某些功能。最值得注意的是,它们不支持与目标相关联的已知类型转换器。请参阅MacDonald, Pro WPF in C#2008 ,p。 872.这就是为什么cwap正确响应我可能需要创建一个类型转换器并在我的自定义按钮的控件模板中专门引用它。
但我不必使用TemplateBinding将控件模板绑定到我的自定义控件的ImagePath属性。我可以使用普通的旧Binding对象。以下是我的自定义控件模板的修订标记:
<!-- Task Button Default Control Template-->
<Style TargetType="{x:Type local:TaskButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TaskButton}">
<StackPanel Height="Auto" Orientation="Horizontal">
<Image Source="{Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}}" Width="24" Height="24" Stretch="Fill" Margin="10,0,0,0" />
<TextBlock Text="{TemplateBinding Text}" HorizontalAlignment="Left" Foreground="{TemplateBinding Foreground}" FontWeight="Bold" Margin="5,0,10,0" VerticalAlignment="Center" FontSize="12" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
如果查看模板中的ImageControl,可以看到更改。请注意同一对象中的RelativeSource属性。将此属性设置为= {RelativeSource TemplatedParent}是允许我在TaskButton的Window1实例中输入相对路径并在自定义控件中正确解析的。
所以我建议其他人研究这个线程,就是跳过值转换器,只需从TemplateBinding切换到Image属性的Binding。
还要感谢Marco Zhou,他在MSDN WPF论坛中提供了this answer类似的问题。
答案 1 :(得分:4)
图片不会将字符串作为源:)您可以在intellisense中看到这一点。您需要绑定ImageSource(或使用IValueConverter将字符串转换为ImageSource)
有关如何进行此转换的一些提示,请参阅this问题。
答案 2 :(得分:2)
实际上这些答案都不正确。
{TemplateBinding ImagePath}
只不过是{Binding Path=ImagePath, RelativeSource={RelativeSource TemplatedParent}}
的捷径,因此几乎完全相同。
此外,如果您为ImagePath
提供字符串,它将正确解析为ImageSource
,但您会在应用程序性能方面受到影响。真正的问题与xaml中提供的ImagePath="Resources\calendar.png"
上的相对和绝对图像路径有关。这使得编译器认为提供的路径是绝对的,因为在定义路径时使用\而不是/。
绑定的长形式和快捷方式不起作用的原因是它为编译器提供了线索,提供的图像源(Resources \ calendar.png)是相对路径而不是绝对路径,因此找到图像并且绑定起作用。如果您调试绑定,您将看到快捷方式尝试将提供的字符串解析为图像源但无法找到文件“Resources \ calendar.png”如果您为图像提供完整的URI,即"C:\...\Resources\calendar.png"
或"/application;component/Resources/calendar.png"
的相应混合符号然后将找到图像并解析绑定。
当您尝试将外部源中的图像而非编译为资源的图像引用到最终编译中时,这一点变得非常重要。
答案 3 :(得分:0)
简单的方法(测试) 1 - 让你的valueConverter像这样
public class objectToImageSourceConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string packUri =value.ToString();
ImageSource Source = new ImageSourceConverter().ConvertFromString(packUri) as ImageSource;
return Source;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
将您的图像源2绑定到父级的字符串properety(我使用“tag”属性) 像这样的xaml:
<Image HorizontalAlignment="Right" Height="Auto" Margin="0,11.75,5.5,10.75" VerticalAlignment="Stretch" Width="40.997" Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"/>