Сегодня, убегая с работы, в спешке перепутал DataGrid и GridView, поэтому не смог показать, как задать шаблон для редактирования ячейки. В общем, исправляюсь и показываю. Ну и чтобы жизнь медом не казалась, пусть еще будет в каждой строке кнопка, которая будет удалять текущую строку.
Итак, задача номер раз. Сделать в таблице возможность редактирования ячейки.
Для демонстрации возьму вот такую структуру данных:
public string Name { get; set; }
public double Percent { get; set; }
ObservableCollection<Item> result = new ObservableCollection<Item>();
result.Add(new Item() { Name = "Параметр 1", Percent = 10 });
result.Add(new Item() { Name = "Параметр 2", Percent = 23 });
result.Add(new Item() { Name = "Параметр 3", Percent = 15.5 });
result.Add(new Item() { Name = "Параметр 4", Percent = 0 });
result.Add(new Item() { Name = "Параметр 5", Percent = 56 });
result.Add(new Item() { Name = "Параметр 6", Percent = 78 });
return result;
}
}
Для показа такого списка, можно воспользоваться вот таким DataGrid:
<DataGridTextColumn Header="Имя" Binding="{Binding Name}" Width="1*" />
<DataGridTextColumn Header="Процент" Width="100" Binding="{Binding Percent,StringFormat={}{0}%}" />
</DataGrid.Columns>
</DataGrid>
Ну и можем инициализировать его данными:
public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
dgMain.ItemsSource = Item.GetItems();
}
}
Запускаем:
Все. Показ работает. Теперь необходимо сделать поддержку редактирования процентов. Для этого заменим TextColumn на TemplateColumn и определим для него не только шаблон показа, но и шаблон редактирования:
<DataGridTemplateColumn Header="Процент" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Percent,StringFormat={}{0}%}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Percent,Mode=TwoWay}" />
<TextBlock Grid.Column="1" Text="%" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Теперь, запустив приложение и дважды кликнув на ячейке мы получим возможность редактирования:
Единственная проблема, которая здесь раздражает, это необходимость двойного клика, чтобы перейти к редактированию. Давайте изменим эту прискорбную ситуацию.
Для этого, установим значение двух свойств и подпишемся на изменение выбранной ячейки у нашего DataGrid:
Обработчик будет иметь вид:
if (e.AddedCells.Count == 0) return;
var currentCell = e.AddedCells[0];
if (currentCell.Column ==
dgMain.Columns[1])
{
dgMain.BeginEdit();
}
}
Все, теперь редактирование будет начинаться по одиночному клику.
Ну и вторая задача, с необходимостью удалить элемент из списка. Для этого, немного изменим код главной формы, добавив свойство, которое будет содержать нашу коллекцию, свойство команды, метод удаления и класс реализующий команду:
public MainWindow()
{
InitializeComponent();
Items = Item.GetItems();
DeleteCommand = new MyCommand() { Collection = Items };
}
public ObservableCollection<Item> Collection { get; set; }
return true;
}
Collection.Remove(parameter as Item);
}
}
get { return (ObservableCollection<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
get { return (ICommand)GetValue(DeleteCommandProperty); }
set { SetValue(DeleteCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for DeleteCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DeleteCommandProperty =
DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(MainWindow), new PropertyMetadata(null));
if (e.AddedCells.Count == 0) return;
var currentCell = e.AddedCells[0];
if (currentCell.Column ==
dgMain.Columns[1])
{
dgMain.BeginEdit();
}
}
}
Добавляем новый столбец, в который помещаем кнопки привязанную к команде:
<DataTemplate>
<Button Content="Х" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Единственной проблемой этого решения, условно можно считать наличие заголовка столбца над кнопками:
Но ведь это же WPF, давайте зададим стиль заголовка таким, чтобы его не было видно на белом фоне:
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}" >
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="BorderBrush" Value="White"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
Теперь DataGrid выглядит так:
Мелочь конечно, но приятно.
На сегодня все, единственно, я приведу полную разметку DataGrid, вдруг выше чего забыл:
ItemsSource="{Binding Path=Items, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Имя" Binding="{Binding Name}" Width="1*" />
<DataGridTemplateColumn Header="Процент" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Percent,StringFormat={}{0}%}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Percent,Mode=TwoWay}" />
<TextBlock Grid.Column="1" Text="%" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}" >
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="BorderBrush" Value="White"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Х" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Итак, задача номер раз. Сделать в таблице возможность редактирования ячейки.
Для демонстрации возьму вот такую структуру данных:
class Item
{public string Name { get; set; }
public double Percent { get; set; }
public static ObservableCollection<Item>
GetItems()
{ObservableCollection<Item> result = new ObservableCollection<Item>();
result.Add(new Item() { Name = "Параметр 1", Percent = 10 });
result.Add(new Item() { Name = "Параметр 2", Percent = 23 });
result.Add(new Item() { Name = "Параметр 3", Percent = 15.5 });
result.Add(new Item() { Name = "Параметр 4", Percent = 0 });
result.Add(new Item() { Name = "Параметр 5", Percent = 56 });
result.Add(new Item() { Name = "Параметр 6", Percent = 78 });
return result;
}
}
Для показа такого списка, можно воспользоваться вот таким DataGrid:
<DataGrid x:Name="dgMain"
AutoGenerateColumns="False" IsReadOnly="True">
<DataGrid.Columns><DataGridTextColumn Header="Имя" Binding="{Binding Name}" Width="1*" />
<DataGridTextColumn Header="Процент" Width="100" Binding="{Binding Percent,StringFormat={}{0}%}" />
</DataGrid.Columns>
</DataGrid>
Ну и можем инициализировать его данными:
public partial class MainWindow : Window
{public MainWindow()
{
InitializeComponent();
Loaded += MainWindow_Loaded;
}
void
MainWindow_Loaded(object sender, RoutedEventArgs e)
{dgMain.ItemsSource = Item.GetItems();
}
}
Запускаем:
Все. Показ работает. Теперь необходимо сделать поддержку редактирования процентов. Для этого заменим TextColumn на TemplateColumn и определим для него не только шаблон показа, но и шаблон редактирования:
<DataGridTemplateColumn Header="Процент" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Percent,StringFormat={}{0}%}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Percent,Mode=TwoWay}" />
<TextBlock Grid.Column="1" Text="%" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Теперь, запустив приложение и дважды кликнув на ячейке мы получим возможность редактирования:
Единственная проблема, которая здесь раздражает, это необходимость двойного клика, чтобы перейти к редактированию. Давайте изменим эту прискорбную ситуацию.
Для этого, установим значение двух свойств и подпишемся на изменение выбранной ячейки у нашего DataGrid:
<DataGrid x:Name="dgMain" AutoGenerateColumns="False" CanUserAddRows="False" SelectionMode="Extended" SelectionUnit="Cell" SelectedCellsChanged="dgMain_SelectedCellsChanged_1">
Обработчик будет иметь вид:
private void
dgMain_SelectedCellsChanged_1(object sender, SelectedCellsChangedEventArgs e)
{if (e.AddedCells.Count == 0) return;
var currentCell = e.AddedCells[0];
if (currentCell.Column ==
dgMain.Columns[1])
{
dgMain.BeginEdit();
}
}
Все, теперь редактирование будет начинаться по одиночному клику.
Ну и вторая задача, с необходимостью удалить элемент из списка. Для этого, немного изменим код главной формы, добавив свойство, которое будет содержать нашу коллекцию, свойство команды, метод удаления и класс реализующий команду:
public partial class MainWindow : Window
{public MainWindow()
{
InitializeComponent();
Items = Item.GetItems();
DeleteCommand = new MyCommand() { Collection = Items };
}
class MyCommand : ICommand
{public ObservableCollection<Item> Collection { get; set; }
public bool
CanExecute(object parameter)
{return true;
}
public event EventHandler CanExecuteChanged;
public void
Execute(object parameter)
{Collection.Remove(parameter as Item);
}
}
public ObservableCollection<Item>
Items
{get { return (ObservableCollection<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
// Using a DependencyProperty as
the backing store for Items. This
enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<Item>), typeof(MainWindow), new PropertyMetadata(null));
public ICommand DeleteCommand
{get { return (ICommand)GetValue(DeleteCommandProperty); }
set { SetValue(DeleteCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for DeleteCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DeleteCommandProperty =
DependencyProperty.Register("DeleteCommand", typeof(ICommand), typeof(MainWindow), new PropertyMetadata(null));
private void
dgMain_SelectedCellsChanged_1(object sender, SelectedCellsChangedEventArgs e)
{if (e.AddedCells.Count == 0) return;
var currentCell = e.AddedCells[0];
if (currentCell.Column ==
dgMain.Columns[1])
{
dgMain.BeginEdit();
}
}
}
Добавляем новый столбец, в который помещаем кнопки привязанную к команде:
<DataGridTemplateColumn Width="30">
<DataGridTemplateColumn.CellTemplate><DataTemplate>
<Button Content="Х" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Единственной проблемой этого решения, условно можно считать наличие заголовка столбца над кнопками:
Но ведь это же WPF, давайте зададим стиль заголовка таким, чтобы его не было видно на белом фоне:
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}" >
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="BorderBrush" Value="White"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
Теперь DataGrid выглядит так:
Мелочь конечно, но приятно.
На сегодня все, единственно, я приведу полную разметку DataGrid, вдруг выше чего забыл:
<DataGrid x:Name="dgMain"
AutoGenerateColumns="False"
CanUserAddRows="False" Margin="5"
SelectionMode="Extended"
SelectionUnit="Cell"
SelectedCellsChanged="dgMain_SelectedCellsChanged_1"ItemsSource="{Binding Path=Items, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Имя" Binding="{Binding Name}" Width="1*" />
<DataGridTemplateColumn Header="Процент" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Percent,StringFormat={}{0}%}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Percent,Mode=TwoWay}" />
<TextBlock Grid.Column="1" Text="%" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="30">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}" >
<Setter Property="Background" Value="White"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
<Setter Property="BorderBrush" Value="White"></Setter>
<Setter Property="BorderThickness" Value="1"></Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Х" Command="{Binding Path=DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" CommandParameter="{Binding}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Здравствуйте , раньше работал с таблицами в winforms загружал данные из MySql в dataGridViev где редактировал нужные параметры и сохранял обратно в MySQL . Всё легко и просто ...
ОтветитьУдалитьВ WPF загружаю данные в dataGrid после чего редактирую нужную ячейку а затем отправляю в MySQL в итоге всё проходит успешно кроме того что в базу приходят старые параметры которые загрузил при получении таблицы
то есть если была строка
параметр 1 - номер1 - 25%
я изменил на
параметр 1 - номер1 - 30%
то в базу данных придёт
параметр 1 - номер1 - 25%
Немного не пойму почему не получается так же как winforms (
Добрый день.
УдалитьДля доступа к базе данных какую технологию используете? ADO?
Добрый день . Прошу прощения , разобрался с проблемой причиной которой было позднее время и моя невнимательность .
ОтветитьУдалитьПо глупости пытался сохранить изменения в Sql при завершении редактирования ячейки (CellEditEnding) .... после чего утром на свежую голову понял что данные в таблицы сохраняются уже после завершения редактирования ячейки . И куда вернее пойти немного другим путём ..
Прошу ещё раз прощение . И спасибо что всё же решили откликнутся на помощь .
Здравствуйте. Полезна была и эта статья и О INotifyPropertyChanged и DependencyProperty. Попытался их соединить : если класс к которому привязываю DataGrid обычный , то всё работает (столбцы нужны 2 string,4 intи главное один CheckBox с bool). А когда создаю класс потомком DependencyObject выскакивает ошибка , как я понял - значение по умолчанию у свойств класса должно быть string. Где и как это можно изменить ? Спасибо.
ОтветитьУдалитьDependencyProperty состоит из двух частей. Динамической и статической. В статической необходимо значение по умолчанию прописывать правильно. В вашем случае, вот так:
Удалитьnew UIPropertyMetadata("")
Так и писал
Удалитьpublic bool li
{
get { return (bool)GetValue(li_svojstvo); }
// set { SetValue(li_svojstvo, value); }
}
public static readonly DependencyProperty li_svojstvo = DependencyProperty.Register("li",
typeof(bool), typeof(cl_столбцы_для_DataGrid), new UIPropertyMetadata(""));
С string всё работает как должно, а bool и int выдаёт ошибку,
А, значит я вас не понял... У вас свойство не типа строка, а типа bool. Значит и значение по умолчанию должно быть bool. Напишите вместо пустой строки - false.
УдалитьСпасибо. я сам был день без инета и смог спокойно подумать и до такого додумался.
УдалитьИскренне понравились Ваши заметки. Какой Вы можете посоветовать материал для начинающего по WPF и XAML ?
Я в свое время в одной из толстых книжек по C# было две главы по WPF. Собственно книг про WPF больше не читал. В основном, если возникает проблема, то читаю MSDN, ищу поисковиками...
УдалитьПонятно как и я.
УдалитьСпасибо за помощь
Пожалуйста. Будут вопросы, задавайте здесь, или на форумах MSDN. Там компания отвечающих хорошая подобралась ;)
УдалитьНе работает поддержка редактирования пока не выключишь IsReadOnly. А если его выключить, то при добавление в последнюю строку записи вы падает следующая строка для записи. Крайне не удобно. как можно это решать?
ОтветитьУдалитьДобрый день.
УдалитьРедактировать записи в отдельном окне (в очень большом количестве случаев это оправдано) или поставьте CanUserAddRows в false.
спасибо большое. А вот у меня небольшой вопрос по DataGrid. Можно ли в таблице менять header столбцов программно? к примеру: я добавляю программно столбцы, в которых должна быть забита информация по различным датам. А дату логичнее всего поставить в заголовок. То есть, чтобы в шапке был datepicker или вроде этого?
УдалитьЭтот комментарий был удален автором.
УдалитьМожно. Что-то XAML в комментарии не вставляется, поэтому вот, картинкой: https://yadi.sk/i/iUxNoM6ccNvj8
УдалитьМожете написать код для редактирования колонки типа CheckBox? Я запутался
ОтветитьУдалитьДа там то же самое. Свойство (обязательно свойство) должно быть типа bool ну и вот здесь пример на столбец такого типа http://msdn.microsoft.com/ru-ru/library/system.windows.controls.datagridcheckboxcolumn(v=vs.110).aspx (в конце)
УдалитьДобрый день! Сделал все по примеру в новом проекте - работает!
ОтветитьУдалитьПри попытке перенести на мой другой проект - не работает, не добавляются позиции в datagrid... при дебаге, компилятор не заходит в class MyCommand : ICommand - может в этом проблема?? Помогите ПЛЗ
Добрый день. Скорее всего проблема в Binding. Убедитесь, что у вас свойство, оно с модификатором public и правильно оно прописано в Binding.
УдалитьУже все возможные варианты с модификаторами доступа перебрал) В XAML ничего не изменял, при этом в маинвиндов добавление работает, но когда я переношу датагрид в другие окна - перестает работать..
УдалитьСкорее всего у вас в DataContext этих окон лежит (если лежит) другой ViewModel у которого нет соответствующей команды...
Удалить