WPF DataGrid에서 한 번 클릭 확인란을 선택하는 방법은 무엇입니까?


143

첫 번째 열이 텍스트 열이고 두 번째 열이 CheckBox 열인 DataGrid가 있습니다. 확인란을 클릭하면 원하는 것입니다. 확인해야합니다.

그러나 선택하려면 두 번의 클릭이 필요합니다. 첫 번째 클릭의 경우 셀이 선택되고 두 번째 클릭의 경우 확인란이 선택됩니다. 한 번의 클릭으로 확인란을 선택 / 선택 취소하는 방법.

WPF 4.0을 사용하고 있습니다. DataGrid의 열은 자동 생성됩니다.


4
의 중복 : stackoverflow.com/questions/1225836/... , 그러나 이것은 더 나은 제목이
surfen

답변:


189

한 번의 클릭으로 DataGrid 확인란의 경우 일반 확인란 컨트롤을 내부에 DataGridTemplateColumn넣고 설정할 수 UpdateSourceTrigger=PropertyChanged있습니다.

<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

4
와우 – 나는 끝까지 읽게되어 기쁘다. 이것은 완벽하게 작동하며 상당히 덜 복잡합니다. IMO는 답변으로 표시해야합니다.
Tod

2
이것은 ComboBox에서도 작동합니다. 에서와 같이 DataGridComboBoxColumn보다 WAY가 좋습니다.
user1454265

2
스페이스 바를 사용하여 확인 / 선택 취소하고 화살표를 사용하여 다른 셀로 이동하면 아닙니다.
Yola

1
나는 당신이 "IsSelected"를 묶어야한다는이 답을 해석했지만 사실이 아닙니다! 당신은 당신의 자신의 바인딩DataGridTemplateColumn.CellTemplate함께 사용할 수 있으며 작동합니다! @ weidian-huang 의 대답 은 그 점을 이해하는 데 도움이되었습니다. 감사합니다!
AstralisSomnium

62

나는 다음 스타일로 이것을 해결했다.

<Style TargetType="DataGridCell">
     <Style.Triggers>
         <Trigger Property="IsMouseOver" Value="True">
             <Setter Property="IsEditing" Value="True" />
         </Trigger>
     </Style.Triggers>
 </Style>

물론 특정 열에 맞게이를 더 조정할 수 있습니다 ...


8
좋은. MultiTrigger로 변경하고 ReadOnly = False에 대한 조건을 추가했지만 기본 방법은 키보드 탐색이 중요하지 않은 간단한 경우에 효과적이었습니다.
MarcE

ItemsSource를 사용하는 동안 해당 스타일을 그리드에 추가하면 Operation 예외가 유효하지 않습니다. 대신 ItemsControl.ItemsSource를 사용하여 요소에 액세스하고 수정하십시오.
Alkampfer

1
이것이 내가 지금까지 본 가장 깨끗한 방법입니다! 좋은! (isReadOnly의를 위해 = "진정한"는 MultiTrigger는 일을 할 것입니다)
FooBarTheLittle

2
이 솔루션에는 예기치 않은 / 원치 않는 동작이 있습니다. 참조 stackoverflow.com/q/39004317/2881450
jHilscher

2
바인딩이 작동하려면 UpdateSourceTrigger = PropertyChanged
AQuirky

27

첫째, 나는 이것이 꽤 오래된 질문이라는 것을 알고 있지만 여전히 시도하고 답할 것이라고 생각했습니다.

나는 며칠 전에 같은 문제가 있었고 놀랍게도 짧은 해결책을 찾았습니다 ( 이 블로그 참조 ). 기본적으로 DataGridCheckBoxColumnXAML 의 정의를 다음으로 바꾸면됩니다.

<DataGridTemplateColumn Header="MyCheckBoxColumnHeader">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <CheckBox HorizontalAlignment="Center" VerticalAlignment="Center" IsChecked="{Binding Path=MyViewModelProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

이 솔루션의 장점은 명백합니다. XAML 전용입니다. 따라서 추가 UI 로직으로 코드 백에 부담을주지 않도록 효과적으로 억제하고 MVVM 열광의 눈에 상태를 유지하는 데 도움이됩니다.).


1
이것은 Konstantin Salavatov의 답변과 비슷 하며이 것이 나를 위해 일했습니다. 그의 코드 샘플을 포함하지 않은 +1. 오래된 질문에 대한 좋은 답변을 주셔서 감사합니다.
Don Herod

1
이 문제는 콤보 상자 열로 수행하면 해당 열의 모든 셀에 항상 작은 드롭 다운 버튼이 표시됩니다. 셀을 클릭 할 때만이 아닙니다.
user3690202

18

확인하려면 콘스탄틴 Salavatov의 응답 으로 작업을 AutoGenerateColumns받는 이벤트 처리기를 추가, DataGridAutoGeneratingColumn다음 코드 :

if (e.Column is DataGridCheckBoxColumn && !e.Column.IsReadOnly)
{
    var checkboxFactory = new FrameworkElementFactory(typeof(CheckBox));
    checkboxFactory.SetValue(FrameworkElement.HorizontalAlignmentProperty, HorizontalAlignment.Center);
    checkboxFactory.SetValue(FrameworkElement.VerticalAlignmentProperty, VerticalAlignment.Center);
    checkboxFactory.SetBinding(ToggleButton.IsCheckedProperty, new Binding(e.PropertyName) { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged });

    e.Column = new DataGridTemplateColumn
        {
            Header = e.Column.Header,
            CellTemplate = new DataTemplate { VisualTree = checkboxFactory },
            SortMemberPath = e.Column.SortMemberPath
        };
}

이렇게하면 DataGrid자동 생성 된 모든 확인란 열을 "단일 클릭"으로 편집 할 수 있습니다.


자동 생성 된 컬럼 접근 방식을 이용해 주셔서 감사합니다.
el2iot2

17

Goblin의 답변에서 참조 된 블로그를 기반으로하지만 .NET 4.0 및 행 선택 모드에서 작동하도록 수정되었습니다.

또한 편집 모드로 들어가서 한 번의 클릭 또는 텍스트 입력시 드롭 다운을 표시하여 DataGridComboBoxColumn 편집 속도를 높입니다.

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;
    }

이 솔루션은 저에게 가장 효과적이었습니다. 내 바인딩 된 ViewModel이 다른 솔루션으로 업데이트되지 않았습니다.
BrokeMyLegBiking

@surfen, 데이터 그리드가있는 많은 페이지가있는 경우 위의 스타일과 코드를 모든 페이지와 코드 숨김에 넣어야합니까? 스타일과 코드를 만드는 대신 일반적인 장소에서 사용할 수 있습니다 모든 페이지
Angel

빈 액션을 파견해야하는 이유는 무엇입니까?
user3690202

@ user3690202 Windows.Forms의 DoEvents와 같습니다. BeginEdit를 호출 한 후 셀이 실제로 편집 모드로 들어갈 때까지 기다려야합니다.
Jiří Skála

@ JiříSkála-이 문제에 대한 솔루션 에서이 작업을 수행 해야하는 것을 기억하지는 않지만 귀하가 말하는 것을 이해합니다-감사합니다!
user3690202

10

나는이 제안들과 다른 사이트에서 찾은 많은 다른 것들을 시도했지만 그중 어느 것도 나를 위해 일하지 않았습니다. 결국 다음과 같은 솔루션을 만들었습니다.

내 DataGrid 상속 컨트롤을 만들고이 코드를 추가했습니다.

public class DataGridWithNavigation : Microsoft.Windows.Controls.DataGrid
{
    public DataGridWithNavigation()
    {
        EventManager.RegisterClassHandler(typeof(DataGridCell), 
            DataGridCell.PreviewMouseLeftButtonDownEvent,
            new RoutedEventHandler(this.OnPreviewMouseLeftButtonDown));
    }


    private void OnPreviewMouseLeftButtonDown(object sender, RoutedEventArgs e)
    {
        DataGridCell cell = sender as DataGridCell;
        if (cell != null && !cell.IsEditing && !cell.IsReadOnly)
        {
          DependencyObject obj = FindFirstControlInChildren(cell, "CheckBox");
            if (obj != null)
            {
                System.Windows.Controls.CheckBox cb = (System.Windows.Controls.CheckBox)obj;
                cb.Focus();
                cb.IsChecked = !cb.IsChecked;
            }
        }
    }

    public DependencyObject FindFirstControlInChildren(DependencyObject obj, string controlType)
    {
        if (obj == null)
            return null;

        // Get a list of all occurrences of a particular type of control (eg "CheckBox") 
        IEnumerable<DependencyObject> ctrls = FindInVisualTreeDown(obj, controlType);
        if (ctrls.Count() == 0)
            return null;

        return ctrls.First();
    }

    public IEnumerable<DependencyObject> FindInVisualTreeDown(DependencyObject obj, string type)
    {
        if (obj != null)
        {
            if (obj.GetType().ToString().EndsWith(type))
            {
                yield return obj;
            }

            for (var i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                foreach (var child in FindInVisualTreeDown(VisualTreeHelper.GetChild(obj, i), type))
                {
                    if (child != null)
                    {
                        yield return child;
                    }
                }
            }
        }
        yield break;
    }
}

이 모든 것이 무엇을합니까?

데이터 그리드에서 셀을 클릭 할 때마다 셀에 CheckBox 컨트롤이 포함되어 있는지 확인합니다. 이 경우 않습니다 , 우리는 그 체크 박스 초점을 설정합니다 그것의 가치 토글을 .

이것은 나에게 효과가있는 것으로 보이며 훌륭하고 쉽게 재사용 가능한 솔루션입니다.

이 작업을 수행하려면 코드를 작성 해야 한다는 점에 실망합니다 . WPF에서 행을 편집 모드로 전환하는 데 사용하기 때문에 첫 번째 마우스 클릭 (DataGrid의 CheckBox에서)이 "무시"된다는 설명은 논리적으로 들릴 수 있지만 실제 환경에서는 모든 실제 응용 프로그램의 작동 방식에 위배됩니다.

화면에 체크 박스가 표시되면 한 번 클릭하여 체크 / 체크 해제 할 수 있어야합니다. 이야기의 끝.


1
고마워, 나는 많은 "솔루션"을 시도했지만 이것이 실제로 매번 작동하는 첫 번째 것입니다. 그리고 그것은 내 응용 프로그램 아키텍처에 아름답게 맞습니다.
Guge

이 솔루션은 바인딩 업데이트 문제를 야기하지만 wpf.codeplex.com/wikipage?title=Single-Click%20 편집 에서는 그렇지 않습니다.
저스틴 시몬

2
너무 복잡한. 내 대답을 참조하십시오. :)
Konstantin Salavatov

1
5 년이 지난 후에도이 코드는 여전히 사회 생활에 필요한 시간을 절약 해줍니다. 필자의 경우 처리기와 동적 이벤트 연결을 달성하기 위해 코드를 Mike의 솔루션과 혼합했으며 그리드에는 동적 열 수가 있으며 특정 셀에서 한 번의 클릭으로 변경 사항을 데이터베이스에 저장해야합니다.
Fer R

8

여기에는 훨씬 간단한 해결책이 있습니다.

          <DataGridTemplateColumn MinWidth="20" >
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <CheckBox VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

DataGridCheckBoxColumn구현 하는 데 사용 하는 경우 첫 번째 클릭은 초점을 맞추고 두 번째 클릭은 확인하는 것입니다.

그러나 DataGridTemplateColumn구현에 사용 하려면 한 번만 클릭하면됩니다.

사용 DataGridComboboxBoxColumn과 구현 의 차이점 DataGridTemplateColumn도 비슷합니다.


나에게 좋은 설명과 즉시 일했습니다, 감사합니다!
AstralisSomnium

8

나는 이것을 해결했다.

<DataGridTemplateColumn>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <Viewbox Height="25">
                <CheckBox IsChecked="{Binding TheProperty, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
            </Viewbox>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

클릭 한 번으로 활성화 된 체크 박스!


2
CheckBox를 ViewBox에 래핑 할 필요는 없지만이 답변이 효과적이었습니다.
JGeerWM

3
이것은 나에게 허용되는 답변보다 훨씬 깨끗한 해결책입니다. Viewbox가 필요하지 않습니다. 정의 된 확인란 열보다 더 잘 작동하는 방법이 재미 있습니다.
kenjara

6

에 자료 짐 아도르노의 대답과 자신의 게시물에 대한 의견이은과 솔루션입니다 MultiTrigger:

<Style TargetType="DataGridCell">
  <Style.Triggers>
    <MultiTrigger>
      <MultiTrigger.Conditions>
    <Condition Property="IsReadOnly" Value="False" />
    <Condition Property="IsMouseOver" Value="True" />
      </MultiTrigger.Conditions>
      <Setter Property="IsEditing" Value="True" />
    </MultiTrigger>
  </Style.Triggers>
</Style>

5

또 다른 간단한 해결책은이 스타일을 DataGridColumn에 추가하는 것입니다. 스타일의 본문은 비어있을 수 있습니다.

<DataGridCheckBoxColumn>
     <DataGridCheckBoxColumn.ElementStyle>
          <Style TargetType="CheckBox">
           </Style>
     </DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>

2
스페이스 바를 누르면 체크 / 체크 해제되어 CheckBox가 왼쪽에서 중간으로 이동합니다. 스타일에 <Setter Property = "HorizontalAlignment"Value = "Center"/>를 추가하면 CheckBox가 이동하지 않습니다.
YantingChen

1
<Style x:Key="StilCelula" TargetType="DataGridCell"> 
<Style.Triggers>
 <Trigger Property="IsMouseOver" Value="True">
   <Setter Property="IsEditing" 
     Value="{Binding RelativeSource={x:Static RelativeSource.Self}, 
     Converter={StaticResource CheckBoxColumnToEditingConvertor}}" />
 </Trigger>
</Style.Triggers>
<Style>
Imports System.Globalization
Public Class CheckBoxColumnToEditingConvertor
    Implements IValueConverter
    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.Convert
        Try

            Return TypeOf TryCast(value, DataGridCell).Column Is DataGridCheckBoxColumn
        Catch ex As Exception
            Return Visibility.Collapsed
        End Try
    End Function

    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As CultureInfo) As Object Implements IValueConverter.ConvertBack
        Throw New NotImplementedException()
    End Function
End Class
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.