如何使用扩展自定义控件并在Windows中使用

3

我希望制作一个类似于Android屏幕的窗口,显示蓝牙、wifi等状态。我有一个检查设备状态并返回0表示关闭、1表示开启和0xFF表示错误的字节值的类。然后我创建了一个按钮(可能应该是ToggleButton...但我对WPF非常陌生)

public class ToggleTaskButton : System.Windows.Controls.Primitives.ButtonBase
{
    public ImageSource ImageSource
    {
        get { return (ImageSource)GetValue(ImageSourceProperty); }
        set { SetValue(ImageSourceProperty, value); }
    }

    public Color MyBackgroundColor
    {
        get { return (Color)GetValue(MyBackgroundColorProperty); }
        set { SetValue(MyBackgroundColorProperty, value); }
    }

    // Using a DependencyProperty as the backing store for MyBackgroundColor.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty MyBackgroundColorProperty =
        DependencyProperty.Register("MyBackgroundColor", typeof(Color), typeof(ToggleTaskButton), new PropertyMetadata(null));


    // Using a DependencyProperty as the backing store for ImageSource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ImageSourceProperty =
        DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ToggleTaskButton), new UIPropertyMetadata(null));
}

关于上述类的注释是,我在考虑是否需要一个依赖属性?相反,我更愿意设置一个值,背景颜色会变为适当的颜色。0 会是灰色,1 会是绿色,>1 会是红色。有一件事我不知道如何做。

然后我制作了一个蓝牙 UserControl,然后将类型改为 ToggleTaskButton。该项目仅为类库,因此我没有获得资源字典:/ 我正在尝试在发布之前使按钮单击部分正常工作。对于混乱造成的不便,敬请谅解。

<ata:ToggleTaskButton x:Class="AdvancedTaskAssigner.Controls.BluetoothControl"
             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:ata="clr-namespace:AdvancedTaskAssigner.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300" Loaded="UserControl_Loaded" Click="ToggleTaskButton_Click"
                      ImageSource="/AdvancedTaskAssigner;component/Resources/Bluetooth.png" MyBackgroundColor="Green">
    <ata:ToggleTaskButton.Resources>
        <Style TargetType="{x:Type ata:ToggleTaskButton}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ata:ToggleTaskButton}">
                        <Viewbox>
                        <Grid>
                            <Border BorderBrush="#FF58595B" BorderThickness="15,15,15,15" CornerRadius="8,8,8,8" >
                                <Border.Background>
                                    <RadialGradientBrush>
                                        <GradientStop Color="#FFB2B2B2" Offset=".75"/>
                                            <GradientStop Offset="1" Color="#FFB2B2B2" />
                                    </RadialGradientBrush>
                                </Border.Background>
                                <Viewbox>
                                    <Image Margin="25" Height="100" Width="100" Source="{TemplateBinding ImageSource}" />
                                </Viewbox>
                            </Border>
                        </Grid>
                        </Viewbox>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ata:ToggleTaskButton.Resources>
</ata:ToggleTaskButton>

代码后台

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace AdvancedTaskAssigner.Controls
{
    /// <summary>
    /// Interaction logic for BluetoothControl.xaml
    /// </summary>
    public partial class BluetoothControl : ToggleTaskButton
    {
        public BluetoothControl()
        {
            InitializeComponent();
            task = new BrightnessTask();
        }

        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            CheckBluetoothState();
            System.Console.Beep(1000, 100);
        }

        private void CheckBluetoothState()
        {
            //bluetoothState = ((byte)task.GetState() == 0x01);
            //Color c = bluetoothState ? (Color)FindResource("enabledColor") : (Color)FindResource("disabledColor");
            //this.outsideColor.Color = c;
        }

        private BrightnessTask task;
        private bool bluetoothState = false;

        private void ToggleTaskButton_Click(object sender, RoutedEventArgs e)
        {
            if (bluetoothState) { task.PerformTaskDown(); MessageBox.Show("BOO"); System.Console.Beep(1000, 100); } //if bluetooth enabled..disable
            else { task.PerformTaskUp(); System.Console.Beep(2000, 100); MessageBox.Show("BOO"); }//if bluetooth disabled..enable.

            CheckBluetoothState();
        }
    }
}

所以我不太确定我在做什么。由于我将要处理各种平板电脑和屏幕尺寸,所以我希望使用WPF。我认为,在加载控件时应该使用蓝牙任务来设置状态。当状态被设置时,它会更改边框背景上的第二个渐变停止的颜色。请帮忙。如何设置GradientStop的颜色?当我将此控件添加到WPF应用程序中的UserControl中时,它什么也没有显示,但在我的设计师中,它显示了这3个按钮之一。

在原则上,可以通过使用“依赖属性”或“可视状态管理器”来简化操作。你想要一个解决问题的替代方案吗?还是你想按照自己写的方式去做? - Anatoliy Nikolaev
@AnatoliyNikolaev 我希望能够解决我的问题。我仍然没有弄清楚如何正确地使其正常工作,但我认为我已经接近了一点。 - Robert Snyder
@AnatoliyNikolaev 我想更好的说法是,因为我在WPF方面非常新手,所以无论是解决我的问题还是提供替代方案都无所谓。 - Robert Snyder
1个回答

1
我创建了一个示例,其中包含一个附加的依赖属性CurrentStatus,该属性包含连接的当前状态。我还为Button触发器创建了一个模板,根据状态设置Button的属性。在Button可以有三种状态,并且添加新状态并不困难。
按钮样式示例:
<Style x:Key="BlueToothButtonStyle" TargetType="{x:Type Button}">
    <Setter Property="Background" Value="Gainsboro" />
    <Setter Property="Foreground" Value="Black" />
    <Setter Property="FontSize" Value="15" />
    <Setter Property="FocusVisualStyle" Value="{x:Null}" />
    <Setter Property="SnapsToDevicePixels" Value="True" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border CornerRadius="2" Background="{TemplateBinding Background}">
                    <Grid>
                        <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
                        <Path x:Name="Bicon" Width="15" Height="27" Stretch="Fill" Fill="#FF000000" Data="F1 M 51,47L 36,61L 36,43L 28.25,50L 25.25,46.75L 35,38L 25.25,29.25L 28.25,26L 36,32L 36,14L 51,29L 42,38L 51,47 Z M 41,43L 41,50.5L 44.5,46.5L 41,43 Z M 41,33L 44.5,29.5L 41,25.3995L 41,33 Z "/>
                    </Grid>
                </Border>

                <ControlTemplate.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(local:DepClass.CurrentStatus)}" Value="1">
                        <Setter Property="Background" Value="Green" />
                    </DataTrigger>

                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(local:DepClass.CurrentStatus)}" Value="2">
                        <Setter Property="Background" Value="Red" />
                        <Setter Property="Foreground" Value="White" />
                    </DataTrigger>

                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Background" Value="#E59400" />
                        <Setter TargetName="Bicon" Property="Fill" Value="White" />                                
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

这里的 DataTrigger 设置了状态的值:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(local:DepClass.CurrentStatus)}" Value="1">
    <Setter Property="Background" Value="Green" />
</DataTrigger>

触发器也可以看起来像这样:

<Trigger Property="local:DepClass.CurrentStatus" Value="1">
    <Setter Property="Background" Value="#E59400" />                            
</Trigger>

这将是相同的。

输出

状态0

enter image description here

状态 1

enter image description here

状态 2

enter image description here

DependencyProperty的列表:

public class DepClass : DependencyObject
{
    public static readonly DependencyProperty CurrentStatusProperty;

    public static void SetCurrentStatus(DependencyObject DepObject, int value)
    {
        DepObject.SetValue(CurrentStatusProperty, value);
    }

    public static int GetCurrentStatus(DependencyObject DepObject)
    {
        return (int)DepObject.GetValue(CurrentStatusProperty);
    }

    static DepClass()
    {
        PropertyMetadata MyPropertyMetadata = new PropertyMetadata(0);

        CurrentStatusProperty = DependencyProperty.RegisterAttached("CurrentStatus",
                                                            typeof(int),
                                                            typeof(DepClass),
                                                            MyPropertyMetadata);
    }        
}

状态是这样定义的:

DepClass.SetCurrentStatus(BluetoothButton, 1);  

或者在 XAML 中像这样:

<Button local:DepClass.CurrentStatus="0" ... />

完整的例子:

XAML

<Window x:Class="BluetoothButtonHelp.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:BluetoothButtonHelp"
    Title="MainWindow" Height="350" Width="525"
    WindowStartupLocation="CenterScreen">

<Window.Resources>
    <Style x:Key="BlueToothButtonStyle" TargetType="{x:Type Button}">
        <Setter Property="Background" Value="Gainsboro" />
        <Setter Property="Foreground" Value="Black" />
        <Setter Property="FontSize" Value="15" />
        <Setter Property="FocusVisualStyle" Value="{x:Null}" />
        <Setter Property="SnapsToDevicePixels" Value="True" />

        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border CornerRadius="2" Background="{TemplateBinding Background}">
                        <Grid>
                            <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
                            <Path x:Name="Bicon" Width="15" Height="27" Stretch="Fill" Fill="#FF000000" Data="F1 M 51,47L 36,61L 36,43L 28.25,50L 25.25,46.75L 35,38L 25.25,29.25L 28.25,26L 36,32L 36,14L 51,29L 42,38L 51,47 Z M 41,43L 41,50.5L 44.5,46.5L 41,43 Z M 41,33L 44.5,29.5L 41,25.3995L 41,33 Z "/>
                        </Grid>
                    </Border>

                    <ControlTemplate.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(local:DepClass.CurrentStatus)}" Value="1">
                            <Setter Property="Background" Value="Green" />
                        </DataTrigger>

                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(local:DepClass.CurrentStatus)}" Value="2">
                            <Setter Property="Background" Value="Red" />
                            <Setter Property="Foreground" Value="White" />
                        </DataTrigger>

                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Background" Value="#E59400" />
                            <Setter TargetName="Bicon" Property="Fill" Value="White" />                                
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<Grid>
    <Button Name="BluetoothButton" Style="{StaticResource BlueToothButtonStyle}" local:DepClass.CurrentStatus="0" Width="40" Height="40" />

    <Button Name="Status1" Content="Status 1" Width="100" Height="30" HorizontalAlignment="Left" Click="Status1_Click" />
    <Button Name="Status2" Content="Status 2" Width="100" Height="30" HorizontalAlignment="Right" Click="Status2_Click" />
    <Button Name="Status0" Content="Status 0" Width="100" Height="30" VerticalAlignment="Top" Click="Status0_Click" />
</Grid>
</Window>

后台代码

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Status1_Click(object sender, RoutedEventArgs e)
    {
        DepClass.SetCurrentStatus(BluetoothButton, 1);
    }

    private void Status2_Click(object sender, RoutedEventArgs e)
    {
        DepClass.SetCurrentStatus(BluetoothButton, 2);
    }

    private void Status0_Click(object sender, RoutedEventArgs e)
    {
        DepClass.SetCurrentStatus(BluetoothButton, 0);
    }
}

public class DepClass : DependencyObject
{
    public static readonly DependencyProperty CurrentStatusProperty;

    public static void SetCurrentStatus(DependencyObject DepObject, int value)
    {
        DepObject.SetValue(CurrentStatusProperty, value);
    }

    public static int GetCurrentStatus(DependencyObject DepObject)
    {
        return (int)DepObject.GetValue(CurrentStatusProperty);
    }

    static DepClass()
    {
        PropertyMetadata MyPropertyMetadata = new PropertyMetadata(0);

        CurrentStatusProperty = DependencyProperty.RegisterAttached("CurrentStatus",
                                                            typeof(int),
                                                            typeof(DepClass),
                                                            MyPropertyMetadata);
    }        
}

1
哇!我对此感到非常惊讶。希望你没有花太长时间来完成它。一旦我实现并且它能够正常工作,我很可能会接受你的答案,因为它看起来正是我所需要的。非常感谢你。 - Robert Snyder
@Robert Snyder:你需要一个例子来解释吗?有什么问题吗? - Anatoliy Nikolaev
昨天我没有机会去处理它。我必须得去处理我的一个C++项目,所以这个得被搁置一下。不过我迫不及待地想试试它。 - Robert Snyder
我已经尝试了一整天,但仍然无法让它正常工作。这可能是因为我的设置方式有问题,因为我不只有一个项目,而是有三个,这让问题变得更加复杂。我打算将重要的部分提取出来,放到另一个解决方案的两个项目中,以便更好地解决这个问题。我真的很困扰,无法让它正常工作。 - Robert Snyder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接