Combobox with custom dropdown(Popup) template and

2019-08-03 17:09发布

问题:

I am having a problem hooking up some events in my custom dropdown or Popup template in my combobox.

This custom template by the way looks like the one in IE 10. Here's the picture I got the partial look (the one below is mine). But I'm having a problem deleting an item in history list. Below is my PART_Popup template

<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
    <Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MinWidth="{Binding ActualWidth, ElementName=Placement}">
        <Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
            <!--<ScrollViewer>-->
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <StackPanel>
                    <ItemsPresenter KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>

                    <Border Padding="5">
                        <StackPanel>
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <TextBlock Text="History" Foreground="{StaticResource SeparatorLine}" />
                                <Border Height="2" Grid.Column="1" Margin="5,0,0,0" BorderBrush="{StaticResource SeparatorLine}" BorderThickness="0.5" />
                            </Grid>
                            <ListBox x:Name="listHistory"  BorderThickness="0" Margin="0" Padding="0" HorizontalContentAlignment="Stretch">
                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Grid>
                                            <TextBlock Text="{Binding }" />
                                            <Button Grid.Column="1" HorizontalAlignment="Right" x:Name="btnDeleteHistoryItem" Content="r" FontFamily="Marlett" Style="{DynamicResource ButtonStyle}" Visibility="Hidden" Opacity="0.75" />
                                        </Grid>

                                        <DataTemplate.Triggers>
                                            <Trigger Property="IsMouseOver" Value="True">
                                                <Setter Property="Visibility" TargetName="btnDeleteHistoryItem" Value="Visible" />
                                            </Trigger>
                                        </DataTemplate.Triggers>
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>
                    </StackPanel>
                    </Border>
                </StackPanel>

                <Border Grid.Row="1" BorderBrush="{StaticResource SeparatorLine}" BorderThickness="0,1,0,0" Padding="5" Height="30">
                    <Border.Background>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                            <GradientStop Color="#33000000" Offset="0"/>
                            <GradientStop Offset="1"/>
                            <GradientStop Offset="0.375"/>
                        </LinearGradientBrush>
                    </Border.Background>

                    <StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
                        <Button x:Name="btnClearHistory" Content=" Clear History " Style="{DynamicResource ButtonStyle}" />
                    </StackPanel>
                </Border>
            </Grid>
            <!--</ScrollViewer>-->
        </Border>
    </Themes:SystemDropShadowChrome>
</Popup>

alright, in the Listbox(listHistory) I have a button there called btnDeleteHistoryItem and I cannot hook it. It's returning a Null error in my code here

protected override void OnSourceInitialized(EventArgs e)
{
    ListBox lb = (ListBox)cbSearch.Template.FindName("listHistory", cbSearch);
    lb.ItemsSource = this.SearchHistory;
    lb.SelectionChanged += cbResults_SelectionChanged;

    Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb);
    // if (btnDeleteHistoryItem != null)
    {
        btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
    }

    // or --------------

    Button btnDeleteHistoryItem = (Button)cbSearch.Template.FindName("btnDeleteHistoryItem", cbSearch);
    // if (btnDeleteHistoryItem != null)
    {
        btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
    }
}

Coding is a little classic here, so no MVVM implemented in hooking with events. So the problem is this Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb); or Button btnDeleteHistoryItem = (Button)cbSearch.Template.FindName("btnDeleteHistoryItem", cbSearch); line where it cannot find that btnDeleteHistoryItem.

How can I resolve this?

-- UPDATE --

lb.Loaded += (a, b) =>
{
    Button btnDeleteHistoryItem = (Button)lb.Template.FindName("btnDeleteHistoryItem", lb);
    //if (btnDeleteHistoryItem != null)
    {
        btnDeleteHistoryItem.Click += DeleteHistoryItem_Click;
    }
};

doesn't work either

回答1:

Using a command will probably be the easiest solution here. You can hook a command up to your delete buttons and pass the current item in as the parameter. Here's a simple sample to illustrate.

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();
        SomeCommand = new MySampleCommand();

        cbo.Items.Add("Hello");
        cbo.Items.Add("Item 1");
        cbo.Items.Add("Another Item");
        cbo.Items.Add("Something else");
        cbo.Items.Add("Yet another item");
    }

    public MySampleCommand SomeCommand { get; set; }
}

public class MySampleCommand : ICommand {
    public bool CanExecute(object parameter) {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter) {
        MessageBox.Show(string.Format("You are trying to remove {0}", parameter.ToString()));
    }
}

Your combobox binding would look something like:

<Window x:Class="SOComboWithEmbeddedButton2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="Window"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <DataTemplate x:Key="ListBoxItemTemplate">
        <StackPanel Orientation="Horizontal">
            <TextBlock Text="{Binding}" />
            <Button Command="{Binding SomeCommand, ElementName=Window}" CommandParameter="{Binding}" Content="Remove" />
        </StackPanel>
    </DataTemplate>
</Window.Resources>
<StackPanel>
    <ComboBox ItemTemplate="{StaticResource ListBoxItemTemplate}" x:Name="cbo" />
</StackPanel>

Just adjust the Command binding on the DataTemplate to point to the appropriate object that has an instance of your command object. Hope that helps!