WPF CellEditingTemplate and duplicated events - wpf

I've a WPF DataGrid with a DataGridTemplateColumn like this:
<DataGridTemplateColumn IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Path=MyProperty, Mode=OneWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
Text="{Binding Path=MyProperty, UpdateSourceTrigger=PropertyChanged}"
TextChanged="ctl_TextChanged" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
I noticed that every time I go into a cell edit, a new textbox control is generated and, consequently, if I start typing a character, the TextChanged event is invoked several times...once for each instance of the control that was generated!
Sample project to reproduce the issue: TestEditingTemplate_4.5.2
I used the TextChanged event only for example, but the issue may also occur with other events (eg. events defined within a UserControl)
Is there is a way to avoid this behavior?
I wish to destroy the "edit" control on CellEditEnding, so that it does not interfere with the new control generated when I return to cell edit; how can I do?

Related

WPF binding query - toggle button in a datagrid

I'm trying to create a column in a DataGrid containing a ToggleButton. The button's IsChecked property is bound to a property called IsSelected in my data item (implements INotifyPropertyChanged).
My first attempt failed to update IsSelected when the button is clicked, even though it correctly displayed the value when set elsewhere.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<ToggleButton IsChecked="{Binding IsSelected}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Searching around stackexchange, I found a couple of solutions:
Setting the UpdateSourceTrigger property of the binding
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<ToggleButton IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
or using relative binding
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="{x:Type local:DataItem}">
<ToggleButton IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridCell}}, Path=DataContext.IsSelected}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
My question is, why does using direct binding require explicitly setting UpdateSourceTrigger, whereas the relative binding method does not?
This is because the DataGrid has an internal transaction behaviour, implemented by BindingGroup, and it generally won't update the source until the edit is committed. If your data object implements the IEditableObject interface, you will notice that its BeginEdit() and EndEdit() methods will be called when you edit a cell value.
Please refer to the following thread on the MSDN forums for more information about this: https://social.msdn.microsoft.com/Forums/vstudio/en-US/7f7196b8-b9dc-487d-93cd-e77f5b3d9906/confused-about-transactional-editing-edititemcanceledit?forum=wpf

How to set up the datagrid control from the Xceed\Extended WPF Toolkit with checkbox column and binding

I'm trying to swap out a WPF datagrid to a xceed\Extended WPF Toolkit DataGridControl.
I need to react to the click event in a checkbox column ... to summarizing a number of other columns.
In the existing datagrid I have a checkbox column, that is bound to a Observable Collection and I call a method if any check box is checked\unchecked. The xaml I use for this, which works, is as such:
<DataGridTemplateColumn Width="40" Header="Inc">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding Include ,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
Checked="CheckBoxUpdated" Unchecked="CheckBoxUpdated" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
For the xceed datagridcontrol I started with the simple syntax below, and the the initial binding seemed OK, but I don't have a click event to respond to:
<xcdg:Column FieldName="Include" Title="Inc" />
Now I tried to do something similar to the original code using the xceed datagridcontrol, as such:
<xcdg:Column FieldName="Include" Title="Inc" Width="*" >
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Include ,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Click="CheckBoxUpdated"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
But I don't think this is the correct syntax. It seems the binding is not working ... based on the initial values of the collection.
(note code behind sets this items source as such dg.ItemsSource = collectionView;)
Any ideas on how to setup a checkbox DataTemplate and the binding correctly?
Thanks
I just found a post at xceed forums that gave me the syntax I needed, that to set the FieldName=".", not FieldName="Include" . My guess is that having FieldName="Include" and the "{Binding Include ..." was confusing the binding.
<xcdg:Column FieldName="." Title="Inc" Width="*" >
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Include ,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Click="CheckBoxUpdated"/>
</DataTemplate>
</xcdg:Column.CellContentTemplate>
Your solution to your question wasn't working for me, what did work however:
Either
<xcdg:Column ...
if the type is boolean it will automaticly create a checkbox for it, you'll have to click 3 times though (column edit -> (un)check -> column out) which can be annoying.
OR
<xcdg:Column FieldName="ckb1" DisplayMemberBinding="{Binding Path=IsThisChecked,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<xcdg:Column.CellContentTemplate>
<DataTemplate>
<CheckBox IsChecked="{xcdg:CellEditorBinding NotifyOnSourceUpdated=True}" HorizontalAlignment="Center" />
</DataTemplate>
</xcdg:Column.CellContentTemplate>
</xcdg:Column>
Which doesn't need all the clicking

WPF DataGrid, in a Binding in a ColumnTemplate access a Element outside of the DataGrid

I'd like to access a Object from my UserControl from within the Datgrids ColumnTemplate.
This doesn't work. Now I've read it's because of the Datacontext.
I found this Example, which should fix this: http://blog.errorok.com/2010/09/09/212/
But the Event: ColumnDataContextChanged is never called in my Project!
here's a part of my XAML:
<DataGridTemplateColumn Header="Database-Fieldtype" Width="Auto" IsReadOnly="False" SortMemberPath="DatabaseFieldType">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DatabaseFieldType}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ConfigurationTool:EditProtocolDatasets}}, Path=grdDatasets.SelectedItem.Storage.DatabaseFieldTypes}"
SelectedItem="{Binding DatabaseFieldType}"
VerticalAlignment="Top" Width="179" Padding="0" Margin="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
ConfigurationTool:EditProtocolDatasets is my UserControl, grdDatasets is another Datagrid, to which SelectedItem I'd like to bind!
Okay, I'm going to suggest a completely different direction than my first one. My guess is that you have the ItemsSource for grdDatasets bound to something.
For the item that's going to act as your datacontext for the control, make sure it has the following characteristics, or at least a comparable structure:
public class ListOfDataSets : DependencyObject
{
public IEnumerable<DataSetOption> Items
{
get
{
...Whatever you normally use to get your DataSetOptions...
}
}
public DataSetOption SelectedItem
{
get { return (DataSetOption)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(DataSetOption), typeof(ListOfDataSets), new PropertyMetadata(null));
}
The key here is that you have one property that is a list of your choices, and another property that represents one of those items.
Then, in your XAML, your control can have the following structure:
<UserControl>
<UserControl.Resources>
<ConfigurationTool:ListOfDatasets x:Key=DataSetOptions />
</UserControl.Resources>
<StackPanel Name="LayoutRoot">
<DataGrid Name="grdDatasets"
ItemsSource="{Binding Source={StaticResource DataSetOptions}, Path=Items}"
SelectedItem="{Binding Source={StaticResource DataSetOptions}, Path=SelectedItem}"
...
</DataGrid>
...
<DataGrid Name="OtherDataGrid" ItemsSource="{Binding OtherSource}">
<DataGrid.Columns>
...
<DataGridTemplateColumn Header="Database-Fieldtype" Width="Auto" IsReadOnly="False" SortMemberPath="DatabaseFieldType">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DatabaseFieldType}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{StaticResource DataSetOptions}, Path=SelectedItem.Storage.DatabaseFieldTypes}" SelectedItem="{Binding DatabaseFieldType, Mode=TwoWay}"
VerticalAlignment="Top" Width="179" Padding="0" Margin="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</Datagrid.Columns>
</DataGrid>
</StackPanel>
</UserControl>
I actually tried this structure out, and the databinding works fine. If the DataSetOptions change a lot, though, this solution may not work, unless you're using MVVM, and the ViewModel is good at tracking what options are available, and presents them properly to the View.
Hopefully this makes sense. I actually tried this one before answering.
I was not correct with my original answer, and overestimated the capabilities of RelativeSource before I experimented with it.
---Original text below---
I think I can help, but I need a few more details. For now I'm just going to work off assumptions.
Assumption 1: You're in a WPF UserControl with a DataGrid that has a defined ItemsSource.
Assumption 2: The UserControl has another element that you want a column within your DataGrid to have access to.
If these two assumptions are correct, it is a much better problem to have in WPF than in Silverlight.
Each row in your DataGrid is going to be working from within a DataContext that consists of the Item for that row. But, you can reach outside of the cell's (or any) DataContext with a RelativeSource.
So, if you wanted to go up the Visual Tree to get to your control's Width, you would use:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MyUserControl}}, Path=Width}
This will trace upward in the Visual Tree until an object of type "MyUserControl" is found, at which point it will grab the "Width" property and bind to it.
The Path doesn't have to be only one item deep, either. You can run up an down your visual tree as required. As this gets more complex, though, your code is going to be more fragile.
If this isn't correct, please post your XAML (or something similar) and say so, and I'll spin up a test environment and edit my post.

How can I access the textbox value which is embeded inside a Silverlight(3.0) Grid?

I have a Silverlight DataGrid control inside which I have a textbox and a button control.
It is as under
<dg:DataGrid x:Name="myGrid" AutoGenerateColumns="False">
<dg:DataGrid.Columns>
<dg:DataGridTemplateColumn Header="Name" Width="100">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}" x:name="txtName"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
<dg:DataGridTemplateColumn Header="Age" Width="100">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Age}" x:name="txtAge"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
<dg:DataGridTemplateColumn Header="Action" Width="100">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnCilck" Content="Click" Click="btnClick_Click />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
</dg:DataGrid.Columns>
</dg:DataGrid>
What I want to do is that at runtime I want to fetch the textbox value (txtName) for the row selected.
I mean, say the grid has 10 rows(i.e. 10 textbox's in that particular column; say Column Name) and 10 Buttons in say Action column(let's name it like that).
Now when I click on the 5th rows Click button, I want to get the value from the textbox present in that row.
Thanks in advance.
In the click event handler you can examine the sender's (Button's) DataContext, which will be the item represented by that row and will have the properties Name, Age etc.; you can get the property which is bound to the textbox.
A better design, assuming you designed your app with MVVM, is to have an ICommand in the ViewModel and bind the Button's Command property to that ICommand. In that case you can bind something to the CommandParameter of the button and receive it in the ICommand handler - either the DataContext itself with {Binding} or the actual property you're interested in.
Edit: sorry about going on with the Command bindings, they're not readily available in SL3; there are ways around it though, google it if you're interested. The commanding pattern will much better encapsulate the actions across your application.
There are actually ways to get to the actual contents of the grid cells, but I wouldn't recommend it, as it will come with a lot of overhead and will be fragile in case any of the templates change; it's much better to work with the actual data and leave the controls to do their jobs through bindings.

WPF Datagrid Template column edit event and alternating column color

I have been using WPF for quiet sometime. I know DataGrid in WPF does not have a Column collection as dependency property so columns cannot be added dynamically.
The application I am developing is highly dynamic so the number of columns are not known. So I am creating DataGridTemplate columns from code behind.
Problem 1 : I want alternating columns to have different background color. How do I do it programatically?? (DataGridTemplateColumn doesnot have a Background property so I am not able to figure out a solution)
Problem 2 : My DataGridTemplateColumn has a DataTemplate in which I have a StackPanel with 2 TextBoxes in it. There is an event in DataGrid called CellEditing Event which fires when we edit a cell. It works for default column, but for my column if I edit those TextBoxes, the event is snot getting fired!!! So how do I achieve it??
(I sometimes get amazed by WPF !!!)
Problem Zero You can have the columns in a datagrid generated for you if you use AutoGenerateColumns="true" when you set up your datagrid. It won't add columns dynamically later, but it might if you reset the itemssource? (not positive on that one)
Problem One DataGrid has properties AlternatingRowBackground and AlternationCount to set up alternating row backgrounds. But i don't see anything for alternating column backgrounds in the grid itself. You could do it inside your datatemplate, though:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Background="Red" Foreground="White">I'm Red</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
But i still see a margin inside that even with margin=0, so the cells look funny if you use any really obvious colors.
Problem Two do you mean the CellEndEditing event? Because i don't see any other cell editing events. I tried the following:
<DataGrid ItemsSource="{Binding}" AutoGenerateColumns="False" CellEditEnding="DataGrid_CellEditEnding">
<DataGrid.Columns>
<DataGridTextColumn Header="A" Binding="{Binding Field0}" />
<DataGridTemplateColumn Header="BC">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Field1}"/>
<TextBlock Text="{Binding Field2}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Field1}"/>
<TextBox Text="{Binding Field2}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
And my DataGrid_CellEditEnding event handler gets called whenever either of the textboxes in the CellEditingTemplate lose focus, whether data changed or not, so things appear to be working for me.
Are you using some other DataGrid than the "built in" WPF one?

Resources