我已经搜索了一下Internet和StackOverflow。为什么需要这样做:我有两个UserControl
和PlusButton
,它们分别具有通用的XAML和背后的通用代码。我想使用一个通用的基类,理想情况下,它也应该具有XAML和代码隐藏功能。
我看到的答案是here,但是我对XAML的经验不足,无法应用该解决方案。我很高兴能链接到官方文档页面,该页面向我学习了我需要执行的操作。我习惯于代码隐藏,因为我也使用WinForms,所以我不介意是否必须进行代码隐藏。
我比XAML更容易理解代码隐藏,因此我有很多代码隐藏,而在XAML中可能会做得更好。
如果我使用发布的解决方案here,则会出现编译器错误:
MinusButton
不能是XAML文件的根目录,因为它是使用XAML定义的。第1行的位置20。- 警告CS0108
cs_wpf_test_1.ArrowButton
隐藏继承的成员PlusButton.InitializeComponent()
。如果要隐藏,请使用new关键字。
我收到上面列出的两个编译器错误,我不知道什么是最正确的解决方法。我希望它易于维护。
下面的代码是我遇到那些编译器错误之前的代码的版本。
ArrowButton.InitializeComponent()
<UserControl x:Class="cs_wpf_test_1.PlusButton"
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:cs_wpf_test_1"
mc:Ignorable="d"
d:DesignHeight="120" d:DesignWidth="175"
Loaded="UserControl_Loaded">
<Button Name="MyButton" Focusable="False" Padding="0,0,0,0">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" CurrentStateChanged="CommonStates_CurrentStateChanged">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="Disabled" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Name="MyCanvas">
<Polyline Stroke="Blue" Name="MyPolyline">
<Polyline.Points>
<PointCollection>
<Point X="5" Y="95" />
<Point X="95" Y="95" />
<Point X="50" Y="5" />
<Point X="5" Y="95" />
</PointCollection>
</Polyline.Points>
</Polyline>
</Canvas>
<Canvas HorizontalAlignment="Center" VerticalAlignment="Center"
Width="100" Height="100" Name="MySecondCanvas">
<Line Stroke="Black" X1="50" Y1="10"
X2="50" Y2="90"
Name="MySignLine1" StrokeThickness="4"/>
<Line Stroke="Black" X1="10" Y1="50"
X2="90" Y2="50"
Name="MySignLine2" StrokeThickness="4"/>
</Canvas>
</Grid>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</UserControl>
与第一个XAML文件相同,但是public partial class PlusButton : UserControl
{
internal Polyline MyTemplatePolyline;
internal Line MyTemplateSignLine1, MyTemplateSignLine2;
internal Canvas MyTemplateCanvas,
MyTemplateSecondCanvas;
public PlusButton()
{
InitializeComponent();
MyButton.Margin = new Thickness(
-MyButton.BorderThickness.Left,
-MyButton.BorderThickness.Top,
-MyButton.BorderThickness.Right,
-MyButton.BorderThickness.Bottom);
}
internal void SetPseudofocused(bool p)
{
if (p)
{
BorderBrush = Brushes.Blue;
}
else
{
BorderBrush = Brushes.Transparent;
}
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
MyButton.ApplyTemplate();
MyTemplatePolyline = (Polyline)MyButton.Template.FindName("MyPolyline", MyButton);
MyTemplateSignLine1 = (Line)MyButton.Template.FindName("MySignLine1", MyButton);
MyTemplateSignLine2 = (Line)MyButton.Template.FindName("MySignLine2", MyButton);
MyTemplateCanvas = (Canvas)MyButton.Template.FindName("MyCanvas", MyButton);
MyTemplateSecondCanvas = (Canvas)MyButton.Template.FindName("MySecondCanvas", MyButton);
UpdateMyLayout();
}
private void CommonStates_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
var btn = e.Control as Button;
if (e.NewState.Name == "MouseOver")
{
btn.Background = Brushes.White;
}
else if (e.NewState.Name == "Pressed")
{
btn.Background = Brushes.LightBlue;
}
else if (e.NewState.Name == "Disabled")
{
btn.Background = Brushes.Gray;
}
else
{
btn.Background = (Brush)MyButton.FindResource(SystemColors.ControlBrushKey);
}
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == WidthProperty || e.Property == HeightProperty || e.Property == BorderThicknessProperty)
{
UpdateMyLayout();
}
}
internal void UpdateMyLayout()
{
if (MyTemplateCanvas == null)
{
return;
}
MyTemplateCanvas.Height = Height;
MyTemplateCanvas.Width = Width;
double h = ActualHeight - BorderThickness.Top -
BorderThickness.Bottom;
double w = ActualWidth - BorderThickness.Left -
BorderThickness.Right;
MyTemplatePolyline.Points.Clear();
MyTemplatePolyline.Points.Add(new Point(5, h - 5));
MyTemplatePolyline.Points.Add(new Point(w - 5, h - 5));
MyTemplatePolyline.Points.Add(new Point(w / 2, 5));
MyTemplatePolyline.Points.Add(new Point(5, h - 5));
h = MyTemplateSecondCanvas.ActualHeight;
w = MyTemplateSecondCanvas.ActualWidth;
double ltrbPadding = h / 3;
double l1 = h - 2 * ltrbPadding;
double l2 = w - 2 * ltrbPadding;
double l = Math.Min(l1, l2);
// draw a cross with two lines:
MyTemplateSignLine1.X1 = l / 2d + ltrbPadding;
MyTemplateSignLine1.X2 = l / 2d + ltrbPadding;
MyTemplateSignLine1.Y1 = ltrbPadding;
MyTemplateSignLine1.Y2 = l + ltrbPadding;
MyTemplateSignLine2.X1 = ltrbPadding;
MyTemplateSignLine2.X2 = l + ltrbPadding;
MyTemplateSignLine2.Y1 = ltrbPadding + l / 2d;
MyTemplateSignLine2.Y2 = ltrbPadding + l / 2d;
// update focus border size:
double v = ActualHeight / 25d;
BorderThickness = new Thickness(v, v, v, v);
}
}
的XAML仅包含一个MySecondCanvas
。
与第一个Line
文件相同,但是:
.cs
和MyTemplateSignLine1
,而仅使用MyTemplateSignLine2
。MyTemplateSignLine
包含其他看起来像向下箭头的点(第一个箭头,上下颠倒)。MyTemplatePolyline
与其他XAML文件中的MyTemplateSignLine
相似,并且位于代码后。答案 0 :(得分:1)
WPF中的继承是一件棘手的事情,如果您只想修改代码,则可以继承CS文件中的控件,并根据需要覆盖方法,但是如果您要修改xaml,它会变得很复杂。 / p>
但是我相信WPF的方式更倾向于样式/模板化。
添加资源字典非常容易(here is how)。 而且,如果您想更改按钮背景,则可以添加一种样式,以在触发时更改它。 而且您可能还希望删除按钮的默认行为,可以通过覆盖模板来做到这一点。
这里有一个您可以重用的按钮样式(负责背景颜色的更改)和模板(没有默认按钮行为)的示例,还可以通过ContentPresenter更改内容:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style x:Key="ColorfulButtonStyle" TargetType="Button">
<Style.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="Pink"/>
</Trigger>
<Trigger Property="IsPressed" Value="False">
<Setter Property="Background" Value="Blue"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Purple"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="green"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="EmptyButtonTemplate" TargetType="Button">
<Border Background="{TemplateBinding Background}" BorderThickness="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ContentPresenter/>
</Border>
</ControlTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Button Template="{StaticResource EmptyButtonTemplate}" Style="{StaticResource ColorfulButtonStyle}" Width="30" Height="30" >
<Ellipse HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="Black"/>
<!--Replace the ellipse with the shape you like, defined as a control in code or in xaml-->
</Button>
答案 1 :(得分:1)
下面的代码是将两个UserControl
统一之后的。
我的XAML:
<UserControl x:Class="cs_wpf_test_1.ArrowButton"
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:cs_wpf_test_1"
mc:Ignorable="d"
d:DesignHeight="120" d:DesignWidth="175"
Loaded="UserControl_Loaded">
<Button Name="MyButton" Focusable="False" Padding="0,0,0,0">
<Button.Resources>
<Style x:Key="styleWithPlusSign">
<Style.Triggers>
<Trigger Property="Grid.Row" Value="0">
<Setter Property="Path.Data" Value="M 5,95 L 95,95 50,5 5,95"></Setter>
</Trigger>
<Trigger Property="Grid.Row" Value="1">
<Setter Property="Path.Data" Value="M 50,10 L 50,10 L 50,90 M 10,50 L 90,50"></Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="styleWithMinusSign">
<Style.Triggers>
<Trigger Property="Grid.Row" Value="0">
<Setter Property="Path.Data" Value="M 5,5 L 50,50 95,5 5,5"></Setter>
</Trigger>
<Trigger Property="Grid.Row" Value="1">
<Setter Property="Path.Data" Value="M 10,50 L 90,50"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Resources>
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates" CurrentStateChanged="CommonStates_CurrentStateChanged">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed" />
<VisualState x:Name="Disabled" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Path Stroke="Blue"
Stretch="Fill"
x:Name="MyFirstPath"
Style="{StaticResource styleWithPlusSign}"
Grid.Row="0"/>
<Path Stroke="Black"
StrokeThickness="1"
Stretch="Uniform"
x:Name="MySecondPath"
Style="{Binding ElementName=MyFirstPath, Path=Style}"
Grid.Row="1"/>
</Grid>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</UserControl>
我的代码隐藏:
/// <summary>
/// Interaction logic for ArrowButton.xaml
/// </summary>
public partial class ArrowButton : UserControl
{
internal Path MyTemplateSecondPath,
MyTemplateFirstPath;
public ArrowButton()
{
InitializeComponent();
// TODO: use smth like MyButton.PropertyChanged to set this:
MyButton.Margin = new Thickness(
-MyButton.BorderThickness.Left,
-MyButton.BorderThickness.Top,
-MyButton.BorderThickness.Right,
-MyButton.BorderThickness.Bottom);
}
public bool State
{
get
{
return (bool)GetValue(StateProperty);
}
set
{
SetValue(StateProperty, value);
}
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(bool),
typeof(ArrowButton), new PropertyMetadata(true, new PropertyChangedCallback(OnStateChanged)));
private static void OnStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var b = d as ArrowButton;
b.MyButton.ApplyTemplate();
b.MyTemplateFirstPath = (Path)b.MyButton.Template.FindName("MyFirstPath", b.MyButton);
if (b.State)
{
// plus
b.MyTemplateFirstPath.Style = b.MyButton.FindResource("styleWithPlusSign") as Style;
}
else
{
// minus
b.MyTemplateFirstPath.Style = b.MyButton.FindResource("styleWithMinusSign") as Style;
}
}
internal void SetPseudofocused(bool p)
{
if (p)
{
BorderBrush = Brushes.Blue;
}
else
{
BorderBrush = Brushes.Transparent;
}
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ApplyTemplate();
MyTemplateSecondPath = (Path)MyButton.Template.FindName("MySecondPath", MyButton);
UpdateMyLayout();
}
private void CommonStates_CurrentStateChanged(object sender, VisualStateChangedEventArgs e)
{
var btn = e.Control as Button;
if (e.NewState.Name == "MouseOver")
{
btn.Background = Brushes.White;
}
else if (e.NewState.Name == "Pressed")
{
btn.Background = Brushes.LightBlue;
}
else if (e.NewState.Name == "Disabled")
{
btn.Background = Brushes.Gray;
}
else
{
btn.Background = (Brush)MyButton.FindResource(SystemColors.ControlBrushKey);
}
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == WidthProperty ||
e.Property == HeightProperty ||
e.Property == BorderThicknessProperty)
{
UpdateMyLayout();
}
}
internal void UpdateMyLayout()
{
if (MyTemplateSecondPath == null)
{
return;
}
// update focus border size:
double v = ActualHeight / 25d;
BorderThickness = new Thickness(v, v, v, v);
double min = Math.Min(ActualWidth, ActualHeight);
if (State)
{
MyTemplateSecondPath.Margin = new Thickness(
min / 5,
min / 5,
min / 5,
min / 5);
}
else
{
MyTemplateSecondPath.Margin = new Thickness(
min / 2.2,
min / 2.2,
min / 2.2,
min / 2.2);
}
}
}