WPF Call Event handler from Style in Xaml

时间:2018-02-03 10:14:05

标签: c# wpf xaml combobox controltemplate

I'm working on a custom WPF combobox control with a template shown in the image below.

enter image description here

As you can see, there is a TextBox (for filtering) and a button (for creating new record). So I create the class AdvComboBox with two events Search, CreateNew. My question is : How to call the handlers in the AdvComboBox class of these events from the control template ?

public class AdvComboBox : ComboBox
{
    public event TextChangedEventHandler Search;

    protected virtual void OnSearch(TextChangedEventArgs e)
    {
        TextChangedEventHandler handler = Search;
        if (handler != null) handler(this, e);
    }


    public event EventHandler CreateNew;

    protected virtual void OnCreateNew()
    {
        EventHandler handler = CreateNew;

        if (handler != null)
            handler(this, EventArgs.Empty);
    }
}

The Popup Part :

<Popup x:Name="PART_Popup" AllowsTransparency="True" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
                            <themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
                                <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">

                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="35"/>
                                            <RowDefinition Height="*"/>
                                            <RowDefinition Height="35"/>
                                        </Grid.RowDefinitions>

                                        <StackPanel Grid.Row="0"
                                                    Orientation="Horizontal">

                                            <!--TODO : Should Call OnSearch-->
                                            <TextBox Width="230"
                                                     Margin="5 0 0 0"
                                                     Height="26"
                                                     VerticalContentAlignment="Center">
                                            </TextBox>
                                        </StackPanel>

                                        <ScrollViewer Grid.Row="1" x:Name="DropDownScrollViewer">
                                            <Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
                                                <Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
                                                    <Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
                                                </Canvas>
                                                <ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
                                            </Grid>
                                        </ScrollViewer>

                                        <StackPanel Grid.Row="2"
                                                    Margin="5">

                                            <!--TODO : Should Call OnCreateNew-->
                                            <Button Content="Create new record"
                                                    Name="BnCreateNew"
                                                    Width="Auto"
                                                    Padding="3"
                                                    HorizontalAlignment="Right">
                                            </Button>

                                        </StackPanel>
                                    </Grid>
                                </Border>
                            </themes:SystemDropShadowChrome>
                        </Popup>

AdvComboBox in Xaml

<local:AdvComboBox 
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        Width="250"
        Height="30"
        VerticalContentAlignment="Center"
        Style="{DynamicResource AdvComboBoxStyle1}"
        Search="AdvComboBox_OnSearch"
        CreateNew="AdvComboBox_OnCreateNew">

        <ComboBoxItem Content="Item 1"/>
        <ComboBoxItem Content="Item 2"/>
        <ComboBoxItem Content="Item 3"/>
        <ComboBoxItem Content="Item 4"/>
        <ComboBoxItem Content="Item 5"/>
    </local:AdvComboBox>

EDIT :

After reading the msdn article provided in @Marco response, I've used Routed events to solve my problem by subscribing to the TextBox.TextChanged event and Button.Click event. Some little logic were necessary in the Button.Click handler to differentiate between the click on the ToggleButton and the simple Button (Create new record).

<local:AdvComboBox 
        x:Name="CbCountries"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        Width="250"
        Height="30"
        VerticalContentAlignment="Center"
        Style="{DynamicResource AdvComboBoxStyle1}"
        TextBox.TextChanged = "AdvComboBox_OnSearch"
        Button.Click = "AdvComboBox_OnCreateNew"/>

Handlers

private void AdvComboBox_OnCreateNew(object sender, RoutedEventArgs e)
    {
        if (e.OriginalSource is ToggleButton)
            return;

        MessageBox.Show("Create new record !", "Hello");
    }

private void AdvComboBox_OnSearch(object sender, RoutedEventArgs e)
    {
        var combobox = sender as AdvComboBox;
        if (combobox == null)
            return;

        var textBox = e.OriginalSource as TextBox;
        if (textBox == null)
            return;

        var itemsViewOriginal = (CollectionView)CollectionViewSource.GetDefaultView(combobox.ItemsSource);

        itemsViewOriginal.Filter = (o =>
        {
            if (String.IsNullOrEmpty(textBox.Text))
                return true;

            if (((string)o).Contains(textBox.Text))
                return true;

            return false;
        });

        itemsViewOriginal.Refresh();
    }

1 个答案:

答案 0 :(得分:1)

您需要将事件清除为RoutedEvents,就像您需要DependencyProperty一样。像这样:

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

创建coltrol并不是那么简单。您还需要为控件添加模板。并添加一个静态构造函数:

    [TemplatePart(Name = "PART_EditableTextBox", Type = typeof(TextBox))]
    [TemplatePart(Name = "PART_Popup", Type = typeof(Popup))]
    [TemplatePart(Name = "PART_CreateNewButton", Type = typeof(Button))]
    [TemplatePart(Name = "PART_SearchTextBox ", Type = typeof(TextBox))]
    [StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(ComboBoxItem))] 
public class AdvComboBox : ComboBox
{ ...

并添加一些属性,如:

for ( int i = 100; i > 0; i-- ) {
  // Some code
}

查看https://msdn.microsoft.com/en-us/library/cc295235.aspx了解详情。