WPF DataGrid'de tek tıklatma onay kutusu seçimi nasıl yapılır?


143

İlk sütun metin sütun ve ikinci sütun CheckBox sütun olarak bir DataGrid var. İstediğim, onay kutusunu tıklarsam. Kontrol edilmelidir.

Ancak, seçilmesi iki tıklama alır, ilk tıklama için hücre seçilir, ikinci tıklama için onay kutusu işaretlenir. Tek bir tıklamayla kontrol / işaretsiz hale getirilmek için onay kutusunun nasıl oluşturulacağı.

WPF 4.0 kullanıyorum. DataGrid öğesindeki sütunlar Otomatik Oluşturulur.


4
Yinelenen: stackoverflow.com/questions/1225836/… , ancak bu daha iyi bir başlık var
surfen

Yanıtlar:


189

Tek tıklamayla DataGrid onay kutusu için içine düzenli onay kutusu kontrolü koyabilir DataGridTemplateColumnve ayarlayabilirsiniz UpdateSourceTrigger=PropertyChanged.

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

4
WOW - sonuna kadar okuduğuma sevindim. Bu mükemmel çalışıyor ve çok daha az karmaşık, IMO bu cevap olarak işaretlenmelidir.
Tod

2
Bu aynı zamanda ComboBox için de geçerlidir. Gibi: yol, DataGridComboBoxColumn daha iyi yol.
user1454265

2
Kontrol etmek / işaretini kaldırmak için boşluk çubuğunu ve başka bir hücreye gitmek için okları kullandığımda değil.
Yola

1
Bu yanıtı "IsSelected" 'a bağlamanız gerektiğini yorumladım, ama bu doğru değil! Sadece kendi BağlamaDataGridTemplateColumn.CellTemplate ile kullanabilirsiniz ve işe yarayacak !! @ Weidian-huang'ın cevabı bunu anlamama yardımcı oldu, teşekkürler!
AstralisSomnium

62

Bunu aşağıdaki Stil ile çözdüm:

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

Elbette bunu belirli sütunlara uyarlamak mümkündür ...


8
Güzel. Bunu bir MultiTrigger olarak değiştirdim ve ReadOnly = False için bir koşul ekledim, ancak temel yaklaşım, klavye navigasyonunun önemli olmadığı basit durumum için çalıştı.
MarcE

Bu stili ızgarama eklemek, bir istisna oluştururken ItemsSource kullanılırken geçerli değildir. Öğelere ItemsControl.ItemsSource ile erişin ve değiştirin.
Alkampfer

1
Bu şimdiye kadar gördüğüm en temiz yol! Güzel! (IsReadOnly = "True" için bir MultiTrigger işi yapacak)
FooBarTheLittle

2
Bu çözümün beklenmedik / istenmeyen davranışları vardır. Bkz. Stackoverflow.com/q/39004317/2881450
jHilscher


27

İlk olarak, bunun oldukça eski bir soru olduğunu biliyorum ama yine de cevaplamaya çalışacağımı sanıyordum.

Birkaç gün önce aynı sorunu yaşadım ve bunun için şaşırtıcı derecede kısa bir çözümle karşılaştım ( bu bloga bakın ). Temel olarak, tek yapmanız gereken DataGridCheckBoxColumnXAML'nizdeki tanımı aşağıdaki ile değiştirmektir:

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

Bu çözümün üst tarafı açık - sadece XAML; böylece etkili bir şekilde ek UI mantığı ile kod geri yük engeller ve MVVM zealots gözünde durumunu korumak yardımcı olur;).


1
Bu Konstantin Salavatov'un cevabına benziyor ve bu benim için çalıştı. Kod örneğini girmediği için +1. Eski bir soruya güzel bir cevap için teşekkürler.
Don Herod

1
Buradaki sorun, birleşik giriş sütunlarıyla yaparsanız, küçük açılır düğmenin o sütundaki tüm hücreler için her zaman görünür olmasıdır. Sadece hücreyi tıkladığınızda değil.
user3690202

18

Yapmak için Konstantin Salavatov cevabı ile çalışmalarını AutoGenerateColumnsbir olay işleyicisi ekleyin DataGrid's AutoGeneratingColumnaşağıdaki kodla:

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

Bu, DataGridotomatik olarak oluşturulan tüm onay kutusu sütunlarının "tek tıklama" düzenlenebilir olmasını sağlar.


Otomatik olarak oluşturulmuş bir sütun yaklaşımını doldurduğunuz için teşekkür ederim, bu beni uygun bir yöne yönlendiriyor.
el2iot2

17

Goblin'in cevabında atıfta bulunulan ancak .NET 4.0 ve Satır Seçim Modunda çalışacak şekilde değiştirilmiş blog'a dayanmaktadır.

Ayrıca, düzenleme moduna girerek ve tek bir tıklama veya metin girişinde açılır liste görüntüleyerek DataGridComboBoxColumn düzenlemesini de hızlandırdığına dikkat edin.

XAML:

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

Kod-arkasında:

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

Bu çözüm benim için en iyi sonucu verdi. Bağlı ViewModel'im diğer çözümlerle güncellenmiyordu.
BrokeMyLegBiking

@surfen, İçinde veri ızgarası olan birçok sayfam varsa yukarıdaki stili ve kodu her sayfaya ve kod arkasına koymam gerekiyor mu? Stil ve kodu, onu oluşturmak yerine ortak bir yerde kullanmak mümkün mü her sayfa
Angel

Neden boş bir İşlem göndermeniz gerekiyor?
user3690202

@ user3690202 Windows.Forms'daki DoEvents gibidir. BeginEdit'i çağırdıktan sonra, hücrenin gerçekten düzenleme moduna girmesini beklemeniz gerekir.
Jiří Skála

@ JiříSkála - Bu soruna yönelik çözümlerimde bunu yapmak zorunda olduğumu hatırlamıyorum, ama ne dediğini anlıyorum - teşekkürler!
user3690202

10

Bu önerileri denedim ve diğer sitelerde bulduğum pek çok şey var, ancak bunların hiçbiri benim için çalıştı. Sonunda, aşağıdaki çözümü yarattım.

Kendi DataGrid devralınmış denetimimi oluşturdum ve sadece bu kodu ekledim:

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

Bütün bunlar ne yapıyor?

DataGrid'imizdeki herhangi bir hücreye her tıkladığımızda, hücrenin içinde bir CheckBox denetimi olup olmadığını görürüz. O takdirde yapar , o zaman o CheckBox odağı oluştururuz ve 's değeri geçiş .

Bu benim için çalışıyor gibi görünüyor ve güzel, kolayca yeniden kullanılabilir bir çözüm.

Bunu yapmak için kod yazmamız gerektiği hayal kırıklığı yaratıyor . WPF satırı Düzenleme moduna koymak için kullandığı için ilk fare tıklamasının (DataGrid'in CheckBox'unda) "yoksayıldığı" açıklaması mantıklı gelebilir, ancak gerçek dünyada bu, her gerçek uygulamanın çalışma biçimine aykırıdır.

Bir kullanıcı ekranında bir onay kutusu görürse, işaretlemek / işaretini kaldırmak için bir kez tıklayabilmelidir. Hikayenin sonu.


1
Teşekkürler, bir sürü "çözüm" denedim, ama bu her seferinde gerçekten işe yarayan ilk şey. Ve uygulama mimarime çok iyi uyuyor.
Guge

Bu çözüm, bağlantının güncellenmesiyle ilgili sorunlara neden olurken, buradakinin çözümü : wpf.codeplex.com/wikipage?title=Single-Click%20Editing bunu yapmaz.
Justin Simon

2
çok karışık. cevabımı gör. :)
Konstantin Salavatov

1
5 yıl sonra bu kod hala sosyal yaşam için zaman tasarrufu :) Bazı basit gereksinimler için @KonstantinSalavatov çözümü yeterlidir. Benim durumumda, işleyicilerle dinamik olay ilişkilendirmesi elde etmek için kodumu Mike'ın çözümüyle karıştırdım, ızgaramın dinamik sütun sayısı var, belirli bir hücredeki tek bir tıklama, değişiklikleri veritabanında saklamalıdır.
Fer R

8

Burada çok daha basit bir çözüm var.

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

DataGridCheckBoxColumnUygulamak için kullanırsanız , ilk tıklama odaklanmak, ikinci tıklama kontrol etmek içindir.

Ancak DataGridTemplateColumnuygulamak için kullanmak tek bir tıklama gerektirir.

Kullanım DataGridComboboxBoxColumnve uygulama arasındaki fark DataGridTemplateColumnda benzerdir.


Benim için iyi bir açıklama ve anında çalıştı, teşekkürler!
AstralisSomnium

8

Bununla çözdüm:

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

Onay kutusu tek tıklamayla etkin!


2
Bir ViewBox onay kutusunu sarmak zorunda değildi, ama bu cevap benim için çalıştı.
Ocak

3
Bu benim için kabul edilen cevaptan çok daha temiz bir çözüm. Viewbox'a da gerek yok. Bunun, tanımlanmış Checkbox sütunundan daha iyi nasıl çalıştığı komik.
kenjara

6

Base Jim Adorno cevap ve onun yazıya yorum bununla çözümdür 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

Yine başka bir basit çözüm, DataGridColumn'unuza bu stili eklemektir. Stilinizin gövdesi boş olabilir.

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

2
Kontrol etmek / işaretini kaldırmak için boşluk çubuğuna basmak, CheckBox'ı soldan ortaya doğru hareket ettirecektir. Tarza <Setter Property = "HorizontalAlignment" Value = "Center" /> eklenmesi CheckBox öğesinin taşınmasını önler.
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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.