根据自定义控件的 DependencyProperty 设置 DockPanel.Dock 不起作用
Setting DockPanel.Dock according to DependencyProperty of a custom control does not work
我有一个枚举:
public enum ImageTextLocation
{
Left,
Top,
Right,
Bottom
}
还有一个自定义 UserControl
,其中包含该枚举的依赖项 属性。
<Button Content="{Binding ButtonText}"
Command="{Binding Command}"
Style="{StaticResource ImageButtonStyle}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ContentPresenter DockPanel.Dock="{Binding Path=TextLocation, Converter={StaticResource EnumToDockConvert}}"
Margin="2,6,0,2"/>
</DockPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
public partial class ImageButtonControl : UserControl
{
public ImageTextLocation TextLocation
{
get => (ImageTextLocation)GetValue(TextLocationProperty);
set => SetValue(TextLocationProperty, value);
}
public static readonly DependencyProperty TextLocationProperty =
DependencyProperty.Register(
"TextLocation",
typeof(ImageTextLocation),
typeof(ImageButtonControl),
new FrameworkPropertyMetadata(ImageTextLocation.Left,
PropertyChangedCallback) { BindsTwoWayByDefault = true, }
);
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var imageButton = (ImageButtonControl)d;
var newLocation = (ImageTextLocation)e.NewValue;
switch (newLocation)
{
case ImageTextLocation.Left:
imageButton.SetCurrentValue(TextLocationProperty, ImageTextLocation.Left);
break;
case ImageTextLocation.Right:
imageButton.SetCurrentValue(TextLocationProperty, ImageTextLocation.Right);
break;
}
}
}
我添加了一个转换器,可以将 ImageTextLocation
转换为 Dock
:
public class EnumToDockConverter : IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
if(value == null)
return Dock.Left;
ImageTextLocation location = (ImageTextLocation)value;
var dock = Enum.Parse(typeof(Dock), location.ToString());
return dock;
}
public object ConvertBack(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
并将转换器实例添加到我的 UserControl
:
的资源中
<coverter:EnumToDockConverter x:Key="EnumToDockConvert"/>
然后,我尝试使用 TextLocation="{Binding ImageTextPosition}"
设置自定义控件的 DockPanel.Dock
,其中 ImageTextPosition
是我的视图模型的字符串 属性 绑定到 UserControl
.
<controls:ImageButtonControl TextLocation="{Binding ImageTextPosition}"/>
当我设置依赖项时,DockPanel 的 Dock 没有设置 属性。
我认为不需要使用转换器。在您的 属性 更改回调中,直接调用 DockPanel.SetDock()
以更改内容演示器的停靠位置。我还将根据 Dock
定义 ImageTextLocation
以便您可以对其进行转换。
更新:OP 澄清了 ImageButtonControl
覆盖了 Button
的默认值 ControlTemplate
,因此我更新了我的答案以在该场景下工作。直接从 Button
派生,而不是 UserControl
,因此模板的内容无需跳过即可访问。
ImageButton.xaml
<!-- bound the content of the Button to TextLocation for testing -->
<Button x:Class="TestApp.ImageButton"
Content="{Binding TextLocation, RelativeSource={RelativeSource Self}}"
Command="{Binding Command}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ContentPresenter x:Name="contentPresenter"
Margin="2,6,0,2"
DockPanel.Dock="Left"/>
<!-- Put something the DockPanel for the content
presenter to dock relative to. Presumably this
would be an image -->
<Button Content="Main Content"/>
</DockPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
代码隐藏
public enum ImageTextLocation
{
Left = (int)Dock.Left,
Top = (int)Dock.Top,
Right = (int)Dock.Right,
Bottom = (int)Dock.Bottom
}
public partial class ImageButton : Button
{
private ContentPresenter _contentPresenter;
public ImageButton()
{
InitializeComponent();
}
public ImageTextLocation TextLocation
{
get => (ImageTextLocation)GetValue(TextLocationProperty);
set => SetValue(TextLocationProperty, value);
}
public static readonly DependencyProperty TextLocationProperty =
DependencyProperty.Register(
nameof(TextLocation),
typeof(ImageTextLocation),
typeof(ImageButton),
new FrameworkPropertyMetadata((d, e) => ((ImageButton)d).SetDock((Dock)e.NewValue)));
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_contentPresenter = (ContentPresenter)GetTemplateChild("contentPresenter");
SetDock((Dock)TextLocation);
}
public void SetDock(Dock newLocation)
{
if (_contentPresenter == null) return; //template not yet applied
DockPanel.SetDock(_contentPresenter, newLocation);
}
}
演示:
<Window x:Class="TestApp.MainWindow">
<UniformGrid>
<local:ImageButton TextLocation="Left" />
<local:ImageButton TextLocation="Right" />
<local:ImageButton TextLocation="Top" />
<local:ImageButton TextLocation="Bottom"/>
</UniformGrid>
</Window>
我有一个枚举:
public enum ImageTextLocation
{
Left,
Top,
Right,
Bottom
}
还有一个自定义 UserControl
,其中包含该枚举的依赖项 属性。
<Button Content="{Binding ButtonText}"
Command="{Binding Command}"
Style="{StaticResource ImageButtonStyle}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="border"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ContentPresenter DockPanel.Dock="{Binding Path=TextLocation, Converter={StaticResource EnumToDockConvert}}"
Margin="2,6,0,2"/>
</DockPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
public partial class ImageButtonControl : UserControl
{
public ImageTextLocation TextLocation
{
get => (ImageTextLocation)GetValue(TextLocationProperty);
set => SetValue(TextLocationProperty, value);
}
public static readonly DependencyProperty TextLocationProperty =
DependencyProperty.Register(
"TextLocation",
typeof(ImageTextLocation),
typeof(ImageButtonControl),
new FrameworkPropertyMetadata(ImageTextLocation.Left,
PropertyChangedCallback) { BindsTwoWayByDefault = true, }
);
private static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var imageButton = (ImageButtonControl)d;
var newLocation = (ImageTextLocation)e.NewValue;
switch (newLocation)
{
case ImageTextLocation.Left:
imageButton.SetCurrentValue(TextLocationProperty, ImageTextLocation.Left);
break;
case ImageTextLocation.Right:
imageButton.SetCurrentValue(TextLocationProperty, ImageTextLocation.Right);
break;
}
}
}
我添加了一个转换器,可以将 ImageTextLocation
转换为 Dock
:
public class EnumToDockConverter : IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
if(value == null)
return Dock.Left;
ImageTextLocation location = (ImageTextLocation)value;
var dock = Enum.Parse(typeof(Dock), location.ToString());
return dock;
}
public object ConvertBack(object value,
Type targetType,
object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
并将转换器实例添加到我的 UserControl
:
<coverter:EnumToDockConverter x:Key="EnumToDockConvert"/>
然后,我尝试使用 TextLocation="{Binding ImageTextPosition}"
设置自定义控件的 DockPanel.Dock
,其中 ImageTextPosition
是我的视图模型的字符串 属性 绑定到 UserControl
.
<controls:ImageButtonControl TextLocation="{Binding ImageTextPosition}"/>
当我设置依赖项时,DockPanel 的 Dock 没有设置 属性。
我认为不需要使用转换器。在您的 属性 更改回调中,直接调用 DockPanel.SetDock()
以更改内容演示器的停靠位置。我还将根据 Dock
定义 ImageTextLocation
以便您可以对其进行转换。
更新:OP 澄清了 ImageButtonControl
覆盖了 Button
的默认值 ControlTemplate
,因此我更新了我的答案以在该场景下工作。直接从 Button
派生,而不是 UserControl
,因此模板的内容无需跳过即可访问。
ImageButton.xaml
<!-- bound the content of the Button to TextLocation for testing -->
<Button x:Class="TestApp.ImageButton"
Content="{Binding TextLocation, RelativeSource={RelativeSource Self}}"
Command="{Binding Command}">
<Button.Template>
<ControlTemplate TargetType="Button">
<Border Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<ContentPresenter x:Name="contentPresenter"
Margin="2,6,0,2"
DockPanel.Dock="Left"/>
<!-- Put something the DockPanel for the content
presenter to dock relative to. Presumably this
would be an image -->
<Button Content="Main Content"/>
</DockPanel>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
代码隐藏
public enum ImageTextLocation
{
Left = (int)Dock.Left,
Top = (int)Dock.Top,
Right = (int)Dock.Right,
Bottom = (int)Dock.Bottom
}
public partial class ImageButton : Button
{
private ContentPresenter _contentPresenter;
public ImageButton()
{
InitializeComponent();
}
public ImageTextLocation TextLocation
{
get => (ImageTextLocation)GetValue(TextLocationProperty);
set => SetValue(TextLocationProperty, value);
}
public static readonly DependencyProperty TextLocationProperty =
DependencyProperty.Register(
nameof(TextLocation),
typeof(ImageTextLocation),
typeof(ImageButton),
new FrameworkPropertyMetadata((d, e) => ((ImageButton)d).SetDock((Dock)e.NewValue)));
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_contentPresenter = (ContentPresenter)GetTemplateChild("contentPresenter");
SetDock((Dock)TextLocation);
}
public void SetDock(Dock newLocation)
{
if (_contentPresenter == null) return; //template not yet applied
DockPanel.SetDock(_contentPresenter, newLocation);
}
}
演示:
<Window x:Class="TestApp.MainWindow">
<UniformGrid>
<local:ImageButton TextLocation="Left" />
<local:ImageButton TextLocation="Right" />
<local:ImageButton TextLocation="Top" />
<local:ImageButton TextLocation="Bottom"/>
</UniformGrid>
</Window>