【问题标题】:DataGridComboBoxColumn - Auto drop down on single clickDataGridComboBoxColumn - 单击时自动下拉
【发布时间】:2011-12-23 23:19:57
【问题描述】:

我在 DataGrid 中有一个 DataGridComboBoxColum。 我希望能够一次单击单元格并下拉组合框。目前我必须点击多次。

  <DataGrid AutoGenerateColumns="False" Height="148" HorizontalAlignment="Left" Margin="48,85,0,0" Name ="dg_display" VerticalAlignment="Top" Width="645"  CanUserAddRows="False" CanUserDeleteRows="False" ItemsSource="{Binding}" SelectionChanged="DgDisplaySelectionChanged">
        <DataGrid.Columns>
            <DataGridTextColumn IsReadOnly="True" Header="Symbol" Binding="{Binding Symbol}" />
            <DataGridTextColumn IsReadOnly="True" Header="Company ID" Binding="{Binding CompanyID}" />
            <DataGridComboBoxColumn IsReadOnly="False" Header="Sector" SelectedValueBinding="{Binding Sector}" DisplayMemberPath="{Binding [0]}" Visibility="Visible" >
                <DataGridComboBoxColumn.EditingElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="ItemsSource" Value="{Binding SectorList}" />
                    </Style>
                </DataGridComboBoxColumn.EditingElementStyle>
                <DataGridComboBoxColumn.ElementStyle>
                    <Style TargetType="ComboBox">
                        <Setter Property="ItemsSource" Value="{Binding SectorList}" />
                    </Style>
                </DataGridComboBoxColumn.ElementStyle>
            </DataGridComboBoxColumn>
        </DataGrid.Columns>
    </DataGrid>

【问题讨论】:

  • 您是否需要DataGrid 才能进入编辑模式,即提高BeginningEditEvent?
  • 我从不提出开始编辑事件。我需要吗?
  • 第一次单击单元格将焦点设置在单元格上并(可能)选择它(取决于 DataGrid 的 SelectionMode),第二次单击显示 EditingElement 并且此时开始编辑事件被提出(由DataGrid)。所以我知道您没有处理此事件,您的逻辑也不取决于DataGrid 是否处于编辑模式(即 IsEditingCurrentCell == true 还是 IsEditingRowItem == true),对吗?
  • 谢谢,很有帮助。

标签: c# wpf xaml datagrid


【解决方案1】:

一键DataGridComboBoxColumn编辑+一键CheckboxColumn编辑
另请参阅: https://stackoverflow.com/a/8333704/724944

XAML:

        <Style TargetType="{x:Type DataGridCell}">
            <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
            <EventSetter Event="PreviewTextInput" Handler="DataGridCell_PreviewTextInput" />
        </Style>

代码隐藏:

    private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private void DataGridCell_PreviewTextInput(object sender, TextCompositionEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        GridColumnFastEdit(cell, e);
    }

    private static void GridColumnFastEdit(DataGridCell cell, RoutedEventArgs e)
    {
        if (cell == null || cell.IsEditing || cell.IsReadOnly)
            return;

        DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
        if (dataGrid == null)
            return;

        if (!cell.IsFocused)
        {
            cell.Focus();
        }

        if (cell.Content is CheckBox)
        {
            if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
            {
                if (!cell.IsSelected)
                    cell.IsSelected = true;
            }
            else
            {
                DataGridRow row = FindVisualParent<DataGridRow>(cell);
                if (row != null && !row.IsSelected)
                {
                    row.IsSelected = true;
                }
            }
        }
        else
        {
            ComboBox cb = cell.Content as ComboBox;
            if (cb != null)
            {
                //DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
                dataGrid.BeginEdit(e);
                cell.Dispatcher.Invoke(
                 DispatcherPriority.Background,
                 new Action(delegate { }));
                cb.IsDropDownOpen = true;
            }
        }
    }


    private static T FindVisualParent<T>(UIElement element) where T : UIElement
    {
        UIElement parent = element;
        while (parent != null)
        {
            T correctlyTyped = parent as T;
            if (correctlyTyped != null)
            {
                return correctlyTyped;
            }

            parent = VisualTreeHelper.GetParent(parent) as UIElement;
        }
        return null;
    }

【讨论】:

  • “另见”是如何做到的。使用 DataGridTemplateColumn 并将其 CellTemplate 设置为 ComboBox。
【解决方案2】:

其他答案都不适合我。对我有用的是以下内容。

<DataGridComboBoxColumn
    Header="Example ComboBox"
    DisplayMemberPath="Name"
    SelectedValuePath="Id">
    <DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="ComboBox">
            <Setter Property="IsDropDownOpen" Value="True" />
        </Style>
    </DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>

我不记得我从哪里得到这个解决方案。它可能来自堆栈溢出。如果有人看到原始来源,请随时发表评论。

【讨论】:

  • 部分有效。我得到了 2 次点击而不是 3 次点击 :(
【解决方案3】:

我对@surfen 的回答有疑问,可能是因为已经过了很多年,而且 WPF 可能已经发生了很大变化。似乎DataGrid 现在会为您处理一些事情,例如在您开始输入时自动编辑文本字段。

我将DataGridTemplateColumn 用于我的组合框列。该模板的CellTemplate 有一个TextBlock。调用BeginEdit 后跟调度程序调用会导致组合框出现在可视树中。然后看起来鼠标点击被发送到组合框并自行打开。

这是我修改后的@surfen 代码:

public static class DataGridExtensions
{
    public static void FastEdit(this DataGrid dataGrid)
    {
        dataGrid.ThrowIfNull(nameof(dataGrid));

        dataGrid.PreviewMouseLeftButtonDown += (sender, args) => { FastEdit(args.OriginalSource, args); };
    }

    private static void FastEdit(object source, RoutedEventArgs args)
    {
        var dataGridCell = (source as UIElement)?.FindVisualParent<DataGridCell>();

        if (dataGridCell == null || dataGridCell.IsEditing || dataGridCell.IsReadOnly)
        {
            return;
        }

        var dataGrid = dataGridCell.FindVisualParent<DataGrid>();

        if (dataGrid == null)
        {
            return;
        }

        if (!dataGridCell.IsFocused)
        {
            dataGridCell.Focus();
        }

        if (dataGridCell.Content is CheckBox)
        {
            if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
            {
                if (!dataGridCell.IsSelected)
                {
                    dataGridCell.IsSelected = true;
                }
            }
            else
            {
                var dataGridRow = dataGridCell.FindVisualParent<DataGridRow>();

                if (dataGridRow != null && !dataGridRow.IsSelected)
                {
                    dataGridRow.IsSelected = true;
                }
            }
        }
        else
        {
            dataGrid.BeginEdit(args);

            dataGridCell.Dispatcher.Invoke(DispatcherPriority.Background, new Action(() => { }));
        }
    }
}

public static class UIElementExtensions
{
    public static T FindVisualParent<T>(this UIElement element)
        where T : UIElement
    {
        UIElement currentElement = element;

        while (currentElement != null)
        {
            var correctlyTyped = currentElement as T;

            if (correctlyTyped != null)
            {
                return correctlyTyped;
            }

            currentElement = VisualTreeHelper.GetParent(currentElement) as UIElement;
        }

        return null;
    }
}

【讨论】:

  • 这么多的代码,只是为了节省用户直观的不必要的点击!感谢您的努力,但恐怕我对 WPF 的看法又下降了。
【解决方案4】:

经过大量研究,最终我发现了这个帖子,Nathan 的解决方案是迄今为止最好的解决方案。然而,如果您单击组合框列,则会打开下拉列表但未选择当前值。

我终于解决了这个问题并减少了代码。请注意,DataTemplate 使用 MouseLeftButtonDown 事件在后面的 UI 代码中初始化“一键魔术”,这要感谢@NathanAldenSr

模板列解决方案的一个很好的副作用是组合框项的数据源可能会每行发生变化,即每行可能有一组不同的组合框项可用。

<DataGridTemplateColumn Header="ComboBox (TemplateColumn)">
    <DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <ComboBox 
                ItemsSource="{Binding DataContext.MyComboBoxItems, 
                       RelativeSource={RelativeSource Mode=FindAncestor,
                       AncestorType=Window}}"
                SelectedValue="{Binding MyComboBoxValue, 
                        Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                IsDropDownOpen="True" />
        </DataTemplate>
    </DataGridTemplateColumn.CellEditingTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock 
                Text="{Binding MyComboBoxValue, Mode=TwoWay,
                            UpdateSourceTrigger=PropertyChanged}" 
                Padding="3" 
                MouseLeftButtonDown="FastEditEvent" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

后面的视图代码是利用NathanAldenSr扩展方法代码:

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

    private void FastEditEvent(object sender, RoutedEventArgs args)
    {
        var dataGridCell = (sender as UIElement)?.FindVisualParent<DataGridCell>();

        dataGridCell.FastEdit(args);
    }
}

public static class DataGridExtensions
{
    public static void FastEdit(this DataGridCell dataGridCell, RoutedEventArgs args)
    {
        if (dataGridCell == null || dataGridCell.IsEditing || dataGridCell.IsReadOnly)
        {
            return;
        }

        var dataGrid = dataGridCell.FindVisualParent<DataGrid>();

        if (dataGrid == null)
        {
            return;
        }

        if (!dataGridCell.IsFocused)
        {
            dataGridCell.Focus();
        }

        dataGrid.Dispatcher.InvokeAsync(() =>
        {
            dataGrid.BeginEdit(args);
        });
    }
}

public static class UiElementExtensions
{
    public static T FindVisualParent<T>(this UIElement element)
        where T : UIElement
    {
        var currentElement = element;

        while (currentElement != null)
        {
            if (currentElement is T correctlyTyped)
            {
                return correctlyTyped;
            }

            currentElement = VisualTreeHelper.GetParent(currentElement) as UIElement;
        }

        return null;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-23
    • 2022-01-21
    • 1970-01-01
    • 2019-09-07
    • 1970-01-01
    • 2018-08-26
    • 2020-11-04
    • 2019-09-24
    相关资源
    最近更新 更多