简体   繁体   English

在 WPF 中创建自定义可重用用户控件,弄清楚如何组合进度条和 slider 条?

[英]Creating a custom reusable user control in WPF, figuring out how to combine the progress bar and the slider bar?

In WPF, I need to create a custom user control.在 WPF 中,我需要创建一个自定义用户控件。 It looks something like this:它看起来像这样:

在此处输入图像描述

Unfortunately, I can't figure out how to create a slider control with a tick, that has the values filled in from an external data source.不幸的是,我不知道如何创建一个带有刻度的 slider 控件,该控件具有从外部数据源填充的值。

In short, the program runs a test, and it displays the results in the user control (shown in the image above).简而言之,程序运行一个测试,并将结果显示在用户控件中(如上图所示)。 The user cannot modify the user control.用户不能修改用户控件。 It is read only.它是只读的。

The colors also change based on the number as well. colors 也会根据数量而变化。 That's self exploratory based on the picture.这是基于图片的自我探索。

I tried creating a slidercontrol and tried to fill in what I need.我尝试创建一个滑块控件并尝试填写我需要的内容。 I also tried to create a progressbar, and trying to draw the tick.我还尝试创建一个进度条,并尝试绘制刻度线。 I know I will have to use databinding for the numbers to be displayed on the user control.我知道我必须使用数据绑定才能在用户控件上显示数字。

Unfortunately, I am at the part where "I don't know where I don't know."不幸的是,我处于“我不知道我不知道的地方”的部分。 I am not that good at creating custom WPF controls (yet).我不擅长创建自定义 WPF 控件(还)。

Can somebody help me create this?有人可以帮我创建这个吗?

Here's one part of the puzzle.这是难题的一部分。 It's not the most generalized thing in the world, but it'll do.这不是世界上最普遍的事情,但它会做。

/// <summary>
/// Bind MidThreshold, HighThreshold, Value
/// </summary>
public class ThresholdBrushConverter : IMultiValueConverter
{
    //  Lame defaults. 
    public Brush LowBrush { get; set; } = Brushes.Red;
    public Brush MidBrush { get; set; } = Brushes.Gold;
    public Brush HighBrush { get; set; } = Brushes.Green;

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        double midthresh = System.Convert.ToDouble(values[0]);
        double highthresh = System.Convert.ToDouble(values[1]);
        double value = System.Convert.ToDouble(values[2]);

        if (value < midthresh)
            return LowBrush;
        else if (value < highthresh)
            return MidBrush;

        return HighBrush;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

XAML XAML

<Window.Resources>
    <local:ThresholdBrushConverter
        x:Key="ThresholdBrushConverter"
        >
        <local:ThresholdBrushConverter.LowBrush>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Offset="0" Color="DarkRed" />
                <GradientStop Offset="0.5" Color="Red" />
                <GradientStop Offset="1" Color="DarkRed" />
            </LinearGradientBrush>
        </local:ThresholdBrushConverter.LowBrush>
        <local:ThresholdBrushConverter.MidBrush>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Offset="0" Color="Gold" />
                <GradientStop Offset="0.5" Color="Yellow" />
                <GradientStop Offset="1" Color="Gold" />
            </LinearGradientBrush>
        </local:ThresholdBrushConverter.MidBrush>
        <local:ThresholdBrushConverter.HighBrush>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
                <GradientStop Offset="0" Color="Green" />
                <GradientStop Offset="0.5" Color="LimeGreen" />
                <GradientStop Offset="1" Color="Green" />
            </LinearGradientBrush>
        </local:ThresholdBrushConverter.HighBrush>
    </local:ThresholdBrushConverter>

...stuff... ...东西...

And in the template:在模板中:

<Border 
    x:Name="ValueBar"
    Width="{Binding ActualWidth, ElementName=DecreaseButton}" 
    HorizontalAlignment="Left" 
    VerticalAlignment="Stretch" 
    >
    <Border.Background>
        <MultiBinding Converter="{StaticResource ThresholdBrushConverter}">
            <Binding Path="MidThreshold" RelativeSource="{RelativeSource TemplatedParent}" />
            <Binding Path="HighThreshold" RelativeSource="{RelativeSource TemplatedParent}" />
            <Binding Path="Value" RelativeSource="{RelativeSource TemplatedParent}" />
        </MultiBinding>
    </Border.Background>
</Border>

MidThreshold and HighThreshold are dependency properties of type double that I added to my Slider subclass. MidThresholdHighThreshold是我添加到 Slider 子类的 double 类型的依赖属性。

<local:MySlider 
    Width="400" 
    Height="20"
    Value="50" 
    Minimum="0" 
    Maximum="100" 
    MidThreshold="50"
    HighThreshold="80"
    />

Here's the MySlider class (I hope you'll think of a less dumb name for your version).这是 MySlider class(我希望你会为你的版本想一个不那么愚蠢的名字)。 I prefer not to write a subclass, but I had to add the Threshold properties.我不想编写子类,但我必须添加阈值属性。 Those could be done as attached properties for a regular slider, but this solution is more to my personal taste.这些可以作为常规 slider 的附加属性来完成,但这个解决方案更符合我的个人口味。

public class MySlider : Slider
{
    static MySlider()
    {
        //  This is so the default style is correctly applied. 
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MySlider), new FrameworkPropertyMetadata(typeof(MySlider)));
    }

    #region MidThreshold Property
    public double MidThreshold
    {
        get { return (double)GetValue(MidThresholdProperty); }
        set { SetValue(MidThresholdProperty, value); }
    }

    public static readonly DependencyProperty MidThresholdProperty =
        DependencyProperty.Register(nameof(MidThreshold), typeof(double), typeof(MySlider),
            new PropertyMetadata(33.0));
    #endregion MidThreshold Property

    #region HighThreshold Property
    public double HighThreshold
    {
        get { return (double)GetValue(HighThresholdProperty); }
        set { SetValue(HighThresholdProperty, value); }
    }

    public static readonly DependencyProperty HighThresholdProperty =
        DependencyProperty.Register(nameof(HighThreshold), typeof(double), typeof(MySlider),
            new PropertyMetadata(66.0));
    #endregion HighThreshold Property

}

I think this is what you need.我认为这就是你所需要的。 What Ed said is very helpful. Ed 说的很有帮助。

One thing that I will add is that I used the Prism MVVM Framework.我要补充的一件事是我使用了 Prism MVVM 框架。 You can read more about Prism here: https://prismlibrary.github.io/您可以在此处阅读有关 Prism 的更多信息: https://prismlibrary.github.io/

ResultBarControl.xaml: ResultBarControl.xaml:

    <UserControl x:Class="ResultBarControl"
             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:ManualControls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
            <LinearGradientBrush x:Key="PassBrush" StartPoint="0.5,0" EndPoint="0.5,1" >
                <GradientStop Color="#287d02" Offset="0.1" />
                <GradientStop Color="#64ce0b" Offset="0.5" />
                <GradientStop Color="#287d02" Offset="1.0" />
            </LinearGradientBrush>
            <LinearGradientBrush x:Key="FailBrush" StartPoint="0.5,0" EndPoint="0.5,1" >
                <GradientStop Color="#db0000" Offset="0.1" />
                <GradientStop Color="#ff5050" Offset="0.5" />
                <GradientStop Color="#db0000" Offset="1.0" />
            </LinearGradientBrush>
            <LinearGradientBrush x:Key="InactiveBrush" StartPoint="0.5,0" EndPoint="0.5,1" >
                <GradientStop Color="#e5e5e5" Offset="0.1" />
                <GradientStop Color="#E3E3E3" Offset="0.5" />
                <GradientStop Color="#cfcfcf" Offset="1.0" />
            </LinearGradientBrush>
            <LinearGradientBrush x:Key="RunningBrush" StartPoint="0.5,0" EndPoint="0.5,1" >
                <GradientStop Color="#ffa600" Offset="0.1" />
                <GradientStop Color="#ffdf2b" Offset="0.5" />
                <GradientStop Color="#ffa600" Offset="1.0" />
            </LinearGradientBrush>

            <Style x:Key="SliderThumbStyle" TargetType="Thumb">
                <Setter Property="SnapsToDevicePixels" Value="true"/>
                <Setter Property="OverridesDefaultStyle" Value="false"/>
                <Setter Property="Height" Value="20"/>
                <Setter Property="Width" Value="15"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Thumb">
                            <Canvas>
                                <Polygon Points="0,0 5,10, 10,0" Stroke="Black" Fill="Black" />
                            </Canvas>

                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

            <Style TargetType="Slider">
                <Setter Property="OverridesDefaultStyle" Value="true"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Slider">
                            <Grid>

                                <Border Name="PART_Border" 
                            BorderThickness="0" 
                            Padding="2"
                            CornerRadius="5" 
                            Width="{TemplateBinding Width}" 
                            Height="12"

                            HorizontalAlignment="Stretch"  
                            VerticalAlignment="Center">
                                    <Border.Style>
                                        <Style TargetType="{x:Type Border}">
                                            <Style.Triggers>
                                                <DataTrigger Binding="{Binding TestStatus}" Value="Pass">
                                                    <Setter Property="Background" Value="{StaticResource PassBrush}"/>
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding TestStatus}" Value="Inactive">
                                                    <Setter Property="Background" Value="{StaticResource InactiveBrush}"/>
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding TestStatus}" Value="Fail">
                                                    <Setter Property="Background" Value="{StaticResource FailBrush}"/>
                                                </DataTrigger>
                                                <DataTrigger Binding="{Binding TestStatus}" Value="Running">
                                                    <Setter Property="Background" Value="{StaticResource RunningBrush}"/>
                                                </DataTrigger>
                                            </Style.Triggers>
                                        </Style>
                                    </Border.Style>
                                </Border>
                                <Track Name="PART_Track" 
                               HorizontalAlignment="Stretch" 
                               VerticalAlignment="Center">
                                    <Track.Thumb>
                                        <Thumb Style="{StaticResource SliderThumbStyle}" />
                                    </Track.Thumb>
                                </Track>
                            </Grid>

                            <ControlTemplate.Triggers>
                                <Trigger Property="Orientation" Value="Vertical">
                                    <Setter TargetName="PART_Border" Property="HorizontalAlignment" Value="Center" />
                                    <Setter TargetName="PART_Border" Property="VerticalAlignment" Value="Stretch" />
                                    <Setter TargetName="PART_Track" Property="HorizontalAlignment" Value="Center" />
                                    <Setter TargetName="PART_Track" Property="VerticalAlignment" Value="Stretch" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>
    <Grid Height="65" Margin="5,2,5,2" >
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20"/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding Title}" Height="25" FontFamily="{DynamicResource TextFont}"  Foreground="#727272" FontSize="15" VerticalAlignment="Bottom"  Grid.Row="0" Grid.Column="0" HorizontalAlignment="Left"/>
        <TextBlock Grid.Row="0" Height="25" FontFamily="{DynamicResource TextFont}"  Foreground="#727272" VerticalAlignment="Bottom" Grid.Column="1" HorizontalAlignment="Right">
            <Run Text="{Binding Value}" FontSize="15" BaselineAlignment="Bottom"  />
            <Run Text="{Binding Unit}" FontSize="13"   BaselineAlignment="Bottom" />
        </TextBlock>
        <Slider Grid.ColumnSpan="2" Height="20" Grid.Row="1" 
                Value="{Binding Value}" 
                Maximum="{Binding Max}" 
                Minimum="{Binding Min}" 
                AutoToolTipPlacement="TopLeft" 
                AutoToolTipPrecision="1"/>
        <TextBlock Grid.Row="2" Grid.Column="0" Height="20"  FontFamily="{DynamicResource TextFont}"  Foreground="#727272" FontSize="12" HorizontalAlignment="Left" VerticalAlignment="Top" Text="{Binding Min}" />
        <TextBlock Grid.Row="2" Grid.Column="1" Height="20"  FontFamily="{DynamicResource TextFont}"  Foreground="#727272" FontSize="12" HorizontalAlignment="Right"  VerticalAlignment="Top" Text="{Binding Max}" />
    </Grid>
</UserControl>

ResultBarViewModel.cs: ResultBarViewModel.cs:

using Prism.Events;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ManualControls
{
    public class ResultBarViewModel : BindableBase
    {
        private string testStatus { get; set; }
        private double? min { get; set; }
        private double? max { get; set; }
        private double? resultvalue { get; set; }
        private string title { get; set; }
        private string unit { get; set; }
        private readonly IEventAggregator _eventAggregator;

        public ResultBarViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _eventAggregator.GetEvent<ReadDataEvent>().Subscribe(RefreshResultData);
        }

        private void RefreshResultData(ReadDataPayload payload)
        {

        }

        public string TestStatus
        {
            get
            {
                return testStatus;
            }
            set
            {
                testStatus = value;
                RaisePropertyChanged();
            }
        }
        public double? Min
        {
            get { return min; }
            set { min = value; }
        }
        public double? Max
        {
            get { return max; }
            set { max = value; }
        }
        public double? Value
        {
            get
            {
                return resultvalue;
            }
            set
            {
                resultvalue = value;
                RaisePropertyChanged();
            }
        }
        public string Title
        {
            get
            {
                return title;
            }
            set
            {
                title = value;
            }
        }
        public string Unit
        {
            get
            {
                return unit;
            }
            set
            {
                unit = value;
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM