[英]WPF Custom Control/Control template
I'm building a wpf application with a custom control and everything worked so far. 我正在构建一个带有自定义控件的wpf应用程序,到目前为止一切正常。
But now i encountered two problems: 但现在我遇到了两个问题:
The template for my custom control (if the code behind is of interest i'll add that as well): 我的自定义控件的模板(如果后面的代码是有意义的,我也会添加它):
<Style TargetType="{x:Type local:MetroProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MetroProgressBar}">
<Grid Background="{TemplateBinding Background}">
<Rectangle Fill="{TemplateBinding ProgressBrush}" HorizontalAlignment="Left"
VerticalAlignment="Stretch" Width="{TemplateBinding ProgressBarWidth}"
Visibility="{TemplateBinding IsHorizontal, Converter={StaticResource BoolToVis}}"/>
<Rectangle Fill="{TemplateBinding ProgressBrush}" HorizontalAlignment="Stretch"
VerticalAlignment="Bottom" Height="{TemplateBinding ProgressBarHeight}"
Visibility="{TemplateBinding IsVertical, Converter={StaticResource BoolToVis}}"/>
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"/>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{TemplateBinding Text}"
FontSize="{TemplateBinding FontSize}" FontStyle="{TemplateBinding FontStyle}" FontWeight="{TemplateBinding FontWeight}"
FontFamily="{TemplateBinding FontFamily}" FontStretch="{TemplateBinding FontStretch}"
Foreground="{TemplateBinding Foreground}" TextWrapping="Wrap"/>
<Polygon Fill="{TemplateBinding BorderBrush}" Points="{TemplateBinding LeftBorderTriangle}"/>
<Polygon Fill="{TemplateBinding BorderBrush}" Points="{TemplateBinding RightBorderTriangle}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The template for the ContentControl: ContentControl的模板:
<vm:RamViewModel x:Key="RamInformationSource"/>
<Style TargetType="ContentControl" x:Key="MemoryUsageTemplate">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid DataContext="{Binding Source={StaticResource RamInformationSource}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock HorizontalAlignment="Center" Text="{Binding DisplayName}" VerticalAlignment="Center"
FontSize="15"/>
<ctrl:MetroProgressBar Grid.Column="1" VerticalAlignment="Stretch" Width="55" Orientation="Vertical" HorizontalAlignment="Center"
ExtenedBorderWidth="0.25" BorderBrush="Gray" Text="Available memory" Progress="{Binding AvailableMemory}"
MaxValue="{Binding TotalMemory}"/>
<ctrl:MetroProgressBar Grid.Column="2" VerticalAlignment="Stretch" Width="60" Orientation="Vertical" HorizontalAlignment="Center"
ExtenedBorderWidth="0.2" BorderBrush="Black" Text="Total memory" Progress="100"
MaxValue="{Binding TotalMemory}"/>
<ctrl:MetroProgressBar Grid.Column="3" VerticalAlignment="Stretch" Width="60" Orientation="Vertical" HorizontalAlignment="Center"
ExtenedBorderWidth="0.2" BorderBrush="DodgerBlue" Text="Used memory" Progress="{Binding UsedMemory}"
MaxValue="{Binding TotalMemory}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The xaml that displays the content: 显示内容的xaml:
...
<ContentControl Style="{StaticResource MemoryUsageTemplate}"/>
<ctrl:MetroProgressBar Grid.Row="1" BorderBrush="Black" Text="Test" HorizontalAlignment="Left" Background="Aquamarine"
Orientation="Horizontal" BorderThickness="2" Height="50" Width="200" Progress="46"/>
<ctrl:MetroProgressBar Grid.Row="1" BorderBrush="Black" Text="Test" HorizontalAlignment="Right"
Orientation="Horizontal" BorderThickness="2" Height="50" Width="200" Progress="46"/>
...
Top of the image shows the content control with the template applied. 图像顶部显示应用了模板的内容控件。 The bottom shows the two progress bars as defined in the last xaml (left with background, right without).
底部显示了最后一个xaml中定义的两个进度条(左边是背景,右边没有)。 That are all custom DPs that are defined for the control:
这些都是为控件定义的自定义DP:
/// <summary>
/// Identifies the ExtenedBorderWidth property.
/// </summary>
public static readonly DependencyProperty ExtenedBorderWidthProperty =
DependencyProperty.Register("ExtenedBorderWidth", typeof(double), typeof(MetroProgressBar),
new PropertyMetadata(0.17, ExtendedBorderWidthValueChanged));
/// <summary>
/// Identifies the Text property.
/// </summary>
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MetroProgressBar), new PropertyMetadata(""));
/// <summary>
/// Identifies the Orientation property.
/// </summary>
public static readonly DependencyProperty OrientationProperty =
DependencyProperty.Register("Orientation", typeof(Orientation), typeof(MetroProgressBar), new PropertyMetadata(Orientation.Horizontal, OrientationValueChanged));
/// <summary>
/// Identifies the IsHorizontal property.
/// </summary>
public static readonly DependencyProperty IsHorizontalProperty =
DependencyProperty.Register("IsHorizontal", typeof(bool), typeof(MetroProgressBar), new PropertyMetadata(true, OrientationChangedByProperty));
/// <summary>
/// Identifies the IsVertical property.
/// </summary>
public static readonly DependencyProperty IsVerticalProperty =
DependencyProperty.Register("IsVertical", typeof(bool), typeof(MetroProgressBar), new PropertyMetadata(false, OrientationChangedByProperty));
/// <summary>
/// Identifies the ProgressBrush property.
/// </summary>
public static readonly DependencyProperty ProgressBrushProperty =
DependencyProperty.Register("ProgressBrush", typeof(Brush), typeof(MetroProgressBar), new PropertyMetadata(new SolidColorBrush(Colors.LightGreen)));
/// <summary>
/// Identifies the LeftBorderTriangle property.
/// </summary>
public static readonly DependencyProperty LeftBorderTriangleProperty =
DependencyProperty.Register("LeftBorderTriangle", typeof(PointCollection), typeof(MetroProgressBar), new PropertyMetadata(new PointCollection()));
/// <summary>
/// Identifies the RightBorderTriangle property.
/// </summary>
public static readonly DependencyProperty RightBorderTriangleProperty =
DependencyProperty.Register("RightBorderTriangle", typeof(PointCollection), typeof(MetroProgressBar), new PropertyMetadata(new PointCollection()));
/// <summary>
/// Identifies the MaxValue property.
/// </summary>
public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register("MaxValue", typeof(ulong), typeof(MetroProgressBar), new PropertyMetadata(100UL, MaxValueChanged));
/// <summary>
/// Identifies the Progress property.
/// </summary>
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register("Progress", typeof(double), typeof(MetroProgressBar), new PropertyMetadata(0.0d, ProgressValueChanged));
/// <summary>
/// Identifies the ProgressBarWidth property.
/// </summary>
public static readonly DependencyProperty ProgressBarWidthProperty
= DependencyProperty.Register("ProgressBarWidth", typeof(double), typeof(MetroProgressBar), new PropertyMetadata(0.0d));
/// <summary>
/// Identifies the ProgressBarHeight property.
/// </summary>
public static readonly DependencyProperty ProgressBarHeightProperty
= DependencyProperty.Register("ProgressBarHeight", typeof(double), typeof(MetroProgressBar), new PropertyMetadata(0.0d));
The DP value changed callbacks and instance methods: DP值更改了回调和实例方法:
#region Static
/// <summary>
/// Changes the orientation based on the calling property.
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="e">Event information.</param>
private static void OrientationChangedByProperty(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
//lock (lockOrientationByProperty)
{
MetroProgressBar pb = source as MetroProgressBar;
if (e.Property == IsVerticalProperty)
{
if ((bool)e.NewValue)
{
pb.IsHorizontal = false;
pb.Orientation = Orientation.Vertical;
}
else
{
pb.IsHorizontal = true;
pb.Orientation = Orientation.Horizontal;
}
}
else
{
// IsVerticalProperty is property that changed
if (!(bool)e.NewValue)
{
pb.IsHorizontal = false;
pb.Orientation = Orientation.Vertical;
}
else
{
pb.IsHorizontal = true;
pb.Orientation = Orientation.Horizontal;
}
}
AdjustVisibleProgressRect(pb);
}
}
/// <summary>
/// Sets the progress value to the new maximum value, if the new max value is less than
/// the current progress value.
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="e">Event information.</param>
private static void MaxValueChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
//lock (lockMaxValue)
{
MetroProgressBar pb = source as MetroProgressBar;
ulong val = Convert.ToUInt64(e.NewValue);
if (val < Convert.ToUInt64(pb.Progress))
{
pb.Progress = val;
// Raise finished event
pb.OnFinished(EventArgs.Empty);
}
}
}
/// <summary>
/// Changes the width of the progress indication rectangle of the progress bar.
/// </summary>
/// <param name="source">The source of the event.</param>
/// <param name="e">Event information.</param>
private static void ProgressValueChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
//lock (lockProgress)
{
MetroProgressBar pb = source as MetroProgressBar;
AdjustVisibleProgressRect(pb, (double)e.NewValue);
pb.OnProgressChanged(new ProgressChangedEventArgs((double)e.NewValue));
// If new progress value equals or is greater than max value raise the finished event
if (pb.MaxValue <= Convert.ToUInt64(e.NewValue))
pb.OnFinished(EventArgs.Empty);
}
}
/// <summary>
/// Changes the width of the progress indication rectangle of the progress bar.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">Event information.</param>
private static void OrientationValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//lock (lockOrientation)
{
MetroProgressBar pb = sender as MetroProgressBar;
pb.AdjustToOrientationChange();
if (pb.Orientation == Orientation.Horizontal)
{
pb.IsVertical = false;
pb.IsHorizontal = true;
}
else
{
pb.IsVertical = true;
pb.IsHorizontal = false;
}
pb.OnOrientationChanged(new OrientationChangedEventArgs((Orientation)e.OldValue, (Orientation)e.NewValue));
}
}
/// <summary>
/// Causes the progress bar to reassign the extended border.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">Event information.</param>
private static void ExtendedBorderWidthValueChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
//lock (lockExtendedBorder)
{
MetroProgressBar pb = sender as MetroProgressBar;
pb.SetUpBorderParts();
}
}
/// <summary>
/// Adjusts the progress bars visible progress rectangles after progress or visible changes.
/// </summary>
/// <param name="pb">The progress bar that changed.</param>
/// <param name="newValue">The new progress value. Only has to be set if there has been a progress change.</param>
private static void AdjustVisibleProgressRect(MetroProgressBar pb, double newValue = -1)
{
if (pb.Orientation == Orientation.Horizontal)
{
pb.ProgressBarWidth = (newValue == -1 ? pb.Progress : newValue) / pb.MaxValue * pb.Width;
}
else
{
pb.ProgressBarHeight = (newValue == -1 ? pb.Progress : newValue) / pb.MaxValue * pb.Height;
}
}
#endregion
#region Non-Static
/// <summary>
/// Adjusts the border ornaments to the new orientation of the control.
/// </summary>
private void AdjustToOrientationChange()
{
SetUpBorderParts();
}
/// <summary>
/// Sets up the triangles that are placed on the left and right side of the progress bar.
/// </summary>
private void SetUpBorderParts()
{
PointCollection leftBorder = new PointCollection();
PointCollection rightBorder = new PointCollection();
double borderWidth = ExtenedBorderWidth;
if (Orientation == Orientation.Horizontal)
{
// Left triangle
leftBorder.Add(new Point(0, 0));
leftBorder.Add(new Point(0, Height));
leftBorder.Add(new Point(Width * borderWidth, 0));
// Right triangle
rightBorder.Add(new Point(Width, 0));
rightBorder.Add(new Point(Width, Height));
rightBorder.Add(new Point(Width - (Width * borderWidth), Height));
}
else
{
// Top border
leftBorder.Add(new Point(0, 0));
leftBorder.Add(new Point(Width, 0));
leftBorder.Add(new Point(0, Height * borderWidth));
// Bottom border
rightBorder.Add(new Point(0, Height));
rightBorder.Add(new Point(Width, Height));
rightBorder.Add(new Point(Width, Height - (Height * borderWidth)));
}
LeftBorderTriangle = leftBorder;
RightBorderTriangle = rightBorder;
}
/// <summary>
/// Raises the Fnished event.
/// </summary>
/// <param name="e">Information on the event.</param>
protected virtual void OnFinished(EventArgs e)
{
EventHandler handler = finished;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Raises the ProgressChanged event.
/// </summary>
/// <param name="e">Information on the event.</param>
protected virtual void OnProgressChanged(ProgressChangedEventArgs e)
{
EventHandler<ProgressChangedEventArgs> handler = progressChanged;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Raises the OrientationChanged event.
/// </summary>
/// <param name="e">Information on the event.</param>
protected virtual void OnOrientationChanged(OrientationChangedEventArgs e)
{
EventHandler<OrientationChangedEventArgs> handler = orientationChanged;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Raises the RenderSizeChanged event and sets up the border parts.
/// </summary>
/// <param name="sizeInfo">Info on the size change.</param>
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
base.OnRenderSizeChanged(sizeInfo);
SetUpBorderParts();
AdjustVisibleProgressRect(this);
}
#endregion
I found the answer to my first problem... Basically the Border element of the progress bar had its Background-property bound to the Control-background and since it is after the Rectangle in the visual tree it overlayed both of them. 我找到了第一个问题的答案......基本上,进度条的Border元素将其Background属性绑定到Control-background,因为它位于可视树中的Rectangle之后,它覆盖了它们。
The second problem occurred because i used Height
and Width
instead of ActualHeight
and ActualWidth
in the code of the user control. 出现第二个问题是因为我在用户控件的代码中使用了
Height
和Width
而不是ActualHeight
和ActualWidth
。 So when working with eg HorizontalAlignment.Stretch
the Width
/ Height
properties are not set and therefore all calculations based on them do not work. 因此,在使用例如
HorizontalAlignment.Stretch
,未设置Width
/ Height
属性,因此基于它们的所有计算都不起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.