gpt4 book ai didi

c# - WPF 自定义控件/控件模板

转载 作者:行者123 更新时间:2023-11-30 18:24:41 25 4
gpt4 key购买 nike

我正在构建一个带有自定义控件的 wpf 应用程序,到目前为止一切正常。
但是现在我遇到了两个问题:

  1. 我想为我的控件指定一个背景颜色,但是它覆盖了网格内的矩形,因此矩形变得不可见。
  2. 我尝试为 ContentControl 编写一个模板,但内容没有按预期呈现,这意味着只有显示名称会与每个进度条的文本一起显示。

我的自定义控件的模板(如果对后面的代码感兴趣,我也会添加它):

<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>

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>

显示内容的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: ContentControl with the template applied, left bottom: custom control with background set, right bottom: custom control with no background set

图像顶部显示了应用了模板的内容控件。底部显示了最后一个 xaml 中定义的两个进度条(左边有背景,右边没有)。这是为控件定义的所有自定义 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));

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

最佳答案

我找到了我的第一个问题的答案...基本上,进度条的 Border 元素将其 Background-property 绑定(bind)到 Control-background,因为它在可视化树中的 Rectangle 之后,它覆盖了它们两个。

第二个问题是因为我在代码中使用了HeightWidth而不是ActualHeightActualWidth用户控件。所以当使用例如Horizo​​ntalAlignment.Stretch Width/Height 属性未设置,因此所有基于它们的计算均无效。

关于c# - WPF 自定义控件/控件模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30980563/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com