WPF Lookless Control Events - PullRequest
       5

WPF Lookless Control Events

2 голосов
/ 30 марта 2010

У меня есть следующий класс:

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 и, таким образом, создавая для него файл с выделенным кодом, что, как я полагаю, вам не нужно делать, как вы обрабатываете события для элементов в вашем xaml, у которого нет свойства Command (это почти все, кроме кнопки)?

Должен ли LooklessControl иметь свой собственный файл XAML (очень похожий на тот, который вы получаете при создании нового UserControl), связанный с ним, который Generic.xaml просто использует как MergedDictionar в качестве шаблона по умолчанию? Или есть какой-то другой признанный способ сделать то, что я пытаюсь сделать?

1 Ответ

3 голосов
/ 31 марта 2010

Чтобы ответить на ваш последний вопрос: НЕТ. Элемент управления без внешнего вида не должен требовать какого-либо известного XAML. Вот что значит безликий.

У вас есть несколько вариантов здесь, но я бы порекомендовал обернуть ваши элементы в кнопки с практически пустым шаблоном управления:

<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

Редактировать: Для того, чтобы кнопки реагировали на щелчки, вам необходимо настроить привязки команд к вашим функциям, которые вы уже определили. Вы бы сделали это как привязку команды класса следующим образом:

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

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

Причина связывания команды класса заключается в том, что если вы добавите ее в коллекцию CommandBindings вашего элемента управления, кто-то, использующий ваш элемент управления, может непреднамеренно удалить их. Также не забудьте обновить методы обработки команд, чтобы иметь статическую семантику.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...