c# - WPF Lookless 控制事件

标签 c# wpf xaml

我有以下类(class):

public class LooklessControl : Control
{
    public List<int> IntList { get; private set; }
    public int CurrentInt { get; private set; }

    private int _index = 0;

    static LooklessControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(LooklessControl), new FrameworkPropertyMetadata(typeof(LooklessControl)));
    }

    public LooklessControl()
    {
        IntList = new List<int>();
        for (int i = 0; i < 10; i++)
        {
            IntList.Add(i);
        }
        CurrentInt = IntList[_index];
    }

    public static readonly RoutedCommand NextItemCommand =
        new RoutedCommand("NextItemCommand", typeof(LooklessControl));

    private void ExecutedNextItemCommand(object sender, ExecutedRoutedEventArgs e)
    {
        NextItemHandler();
    }

    private void CanExecuteNextItemCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }

    public static readonly RoutedCommand PrevItemCommand =
        new RoutedCommand("PrevItemCommand", typeof(LooklessControl));

    private void ExecutedPrevItemCommand(ExecutedRoutedEventArgs e)
    {
        PrevItemHandler();
    }

    private void CanExecutePrevItemCommand(object sender, CanExecuteRoutedEventArgs e)
    {
        e.CanExecute = true;
    }


    public static readonly RoutedEvent NextItemEvent =
        EventManager.RegisterRoutedEvent("NextItemEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(LooklessControl));

    public event RoutedEventHandler NextItem
    {
        add { AddHandler(NextItemEvent, value); }
        remove { RemoveHandler(NextItemEvent, value); }
    }

    private void RaiseNextItemEvent()
    {
        RoutedEventArgs args = new RoutedEventArgs(LooklessControl.NextItemEvent);
        RaiseEvent(args);
    }

    public static readonly RoutedEvent PrevItemEvent =
        EventManager.RegisterRoutedEvent("PrevItemEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(LooklessControl));

    public event RoutedEventHandler PrevItem
    {
        add { AddHandler(PrevItemEvent, value); }
        remove { RemoveHandler(PrevItemEvent, value); }
    }

    private void RaisePrevItemEvent()
    {
        RoutedEventArgs args = new RoutedEventArgs(LooklessControl.PrevItemEvent);
        RaiseEvent(args);
    }

    private void NextItemHandler()
    {
        _index++;
        if (_index == IntList.Count)
        {
            _index = 0;
        }

        CurrentInt = IntList[_index];
        RaiseNextItemEvent();
    }

    private void PrevItemHandler()
    {
        _index--;
        if (_index == 0)
        {
            _index = IntList.Count - 1;
        }

        CurrentInt = IntList[_index];
        RaisePrevItemEvent();
    }
}

该类在 Generic.xaml 中具有默认样式,如下所示:

<Style x:Key="{x:Type local:LooklessControl}" TargetType="{x:Type local:LooklessControl}">
    <Setter Property="Height" Value="200"/>
    <Setter Property="Width" Value="90"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:LooklessControl}">
                <Border BorderBrush="Black" BorderThickness="1" Padding="2">
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="20"/>
                            <RowDefinition Height="*"/>
                        </Grid.RowDefinitions>
                        <Rectangle Grid.Row="0" Fill="LightGray"/>
                        <Rectangle Grid.Row="1" Fill="Gainsboro"/>
                        <Grid Grid.Row="0">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="10"/>
                                <ColumnDefinition Width="*"/>
                                <ColumnDefinition Width="10"/>
                            </Grid.ColumnDefinitions>
                            <Path Grid.Column="0" x:Name="pathLeftArrow" Data="M0,0.5 L1,1 1,0Z" Width="6" Height="14" Stretch="Fill"
                                  HorizontalAlignment="Center" Fill="SlateBlue"/>
                            <TextBlock Grid.Column="1" Name="textBlock" 
                                   Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CurrentInt}" 
                                   HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Junction" FontSize="13"/>
                            <Path Grid.Column="2" x:Name="pathRightArrow" Data="M0,0 L1,0.5 0,1Z" Width="6" Height="14" Stretch="Fill"
                                  HorizontalAlignment="Center" Fill="SlateBlue"/>
                        </Grid>
                        <ListBox Grid.Row="1" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" Background="Transparent" 
                             ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IntList}"/>
                    </Grid>
                </Border>                    
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

如何做到这样,当用户单击 pathLeftArrow 时,它会触发 LooklessControl.PrevItemCommand,或者他们单击 pathRightArrow 时,它会触发 LooklessControl.NextItemCommand,或者他们单击 ListBox 中的某个项目,并且 LooklessControl 会收到通知新选择的项目?

换句话说,如果不将 x:Class 添加到 Generic.xaml 的顶部并为其创建一个代码隐藏文件(我假设您不想这样做),那么如何处理您的元素中的事件没有 Command 属性的 xaml(它几乎是除 Button 之外的所有属性)?

LooklessControl 是否应该拥有与其关联的自己的 XAML 文件(很像您在创建新 UserControl 时获得的文件),而 Generic.xaml 仅将其作为 MergedDictionar 作为其默认模板引入?或者还有其他公认的方法来做我想做的事情吗?

最佳答案

回答你的最后一个问题:不。外观控件不需要任何已知的 XAML。这就是“lookless”的意思。

这里有几个选项,但我建议使用基本上空的控件模板将元素包装在按钮中:

<ControlTemplate x:Key="contentOnlyButton" TargetType="{x:Type Button}">
    <ContentPresenter />
</ControlTemplate>

...

   <Button Grid.Column="0" Template="{StaticResource contentOnlyButton}"
           Command="{x:Static local:LooklessControl.PrevItemCommand}">
       <Path x:Name="pathLeftArrow" Data="M0,0.5 L1,1 1,0Z" Width="6" Height="14" 
             Stretch="Fill" HorizontalAlignment="Center" Fill="SlateBlue"/>
   </Button>

您的另一个选择(我想说这可能不是您在单击时执行命令应该做的事情,但可能适用于其他情况),是在 OnApplyTemplate 中查找模板中的指定部分,然后连线上事件。

public override void OnApplyTemplate()
{
    var prevElement = this.GetTemplateChild("PART_PathLeftArrow") as UIElement;
    if (prevElement != null)
        prevElement.MouseDown += (o, e) => PrevItemHandler();
    ...
}

执行此操作时需要注意的一件事是,模板不需要定义您正在查找的部分,因此您需要优雅地检查该情况。对于意外删除所需元素的设计人员/开发人员来说,在此处抛出 NullReferenceExceptions 将使重新设计控件变得非常痛苦。您还需要遵循使用 PART_ 语法命名所需元素的标准做法,并使用 TemplatePart 属性装饰您的类。

[TemplatePart(Name = "PART_PathLeftArrow", Type = typeof(UIElement))]
[TemplatePart(Name = "PART_PathRightArrow", Type = typeof(UIElement))]
...
public class LooklessControl : Control

编辑:为了使按钮响应点击,您需要将 CommandBindings 设置为您已定义的函数。您可以将其作为类命令绑定(bind)来执行,如下所示:

static LooklessControl()
{
    CommandManager.RegisterClassCommandBinding(
        typeof(LooklessControl),
        new CommandBinding(NextItemCommand, ExecutedNextItemCommand, CanExecuteNextItemCommand));

    CommandManager.RegisterClassCommandBinding(
        typeof(LooklessControl),
        new CommandBinding(PrevItemCommand, ExecutedPrevItemCommand, CanExecutePrevItemCommand));
}

进行类命令绑定(bind)的原因是,如果将其添加到控件的 CommandBindings 集合中,使用您的控件的人可能会无意中删除它们。另请记住更新您的命令处理方法以具有静态语义。

关于c# - WPF Lookless 控制事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2548116/

相关文章:

c# - RavenDB 使用 JsonIgnore 属性存储属性

c# - WPF MVVM 我可以从 View 中使用模型吗

wpf - ViewModel 中的依赖属性有什么例子吗?

c# - 创建特定的动态视觉效果 - 带对象的房屋计划(后续)

wpf - 通过XAML将Window.Content设置为页面吗?

c# - 如何向 xaml 中的绑定(bind)添加附加文本(或字符串)

c# - 如何修复 Soap 中的绑定(bind)失败?

c# - 使用 NewtonSoft - 数组将 JSON 反序列化为 .net 对象

c# - 使用 jQuery 和 Asp.Net 上传文件时出错

c# - 如何为模板控件创建自定义属性