I have a ListView with different DataTemplates set up to bind. I want to update some of the columns (mix of textblock and progress bar) when I call UpdateSource() on a bindingexpression. I also want to update 1 of the columns when the property is changed which it is bound too.
I was able to get the PropertyChanged behaviour to work. But whenever I change the property of one of the other columns they update straight await, instead of on the UpdateSource call. It would appear that its ignoring the UpdateSourceTrigger which is set in the xaml and is using the default behaviour.
I have a class which implements the INotifyPropertyChanged interface.
The xaml for the column I want to update explicitly looks like this:
<GridViewColumn Width="300" Header="Percentage" DisplayMemberBinding="{Binding Percentage, UpdateSourceTrigger=Explicit}" />
And the xaml for one which I want to update on property change:
<GridViewColumn Header="Status" Width="150" DisplayMemberBinding="{Binding Status, UpdateSourceTrigger=PropertyChanged}" />
My binding is set originally like so:
Binding downloadBinding = new Binding();
downloadBinding.Source = _downloads;
ListDownloads.SetBinding(ListView.ItemsSourceProperty, downloadBinding);
If I execute the following code:
_downloads[0].Percentage += 0.3;
_downloads[0].FileSize = 700.00;
_downloads[1].Percentage += 10;
The column percentage column is updated straight away, but I would expect it to wait for the call on UpdateSource().
My code for updating the source is:
BindingExpression be = ListDownloads.GetBindingExpression(ListView.ItemsSourceProperty);
be.UpdateSource();
Am I missing something? I can't find anything online or in a book about why this is happening.
Cheers
I think you're confusing the source and the target... the source is your _downloads collection, the target is the GridViewColumn. The UpdateSourceTrigger property controls when the source is updated by the control, but I assume your grid it not editable, so you don't need to use this property.
The update of the target is always immediate, there is no UpdateTargetTrigger property...
Related
A reoccurring issue I have is needing to create enhanced text columns for datagrids. By that I mean columns that act just like normal text columns, but with an additional graphic or feature, like an image displayed next to the text. So I'm using template columns, but apparently this means having to "start from scratch" in generating a lot of the features expected of a normal text column, such as the textbox editing template:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding Path=[binded text], Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
I want to define a column that's inherited from DataGridTemplateColumn, then dump all this code into it, so I can reuse these columns with any datagrid I wish. But as shown above, I can't declare the binding in the class definition because that obviously depends upon usage.
How can I define an inherited datagrid column that makes use of child controls (specifically the cell editing textbox in this case), but still allows binding to be set for these controls when the column has been declared with xaml inside some actual datagrid?
So far I've tried to expose a method to do this, but it's not working:
Public Class MyTextColumn
Inherits DataGridTemplateColumn
....
Public Property EditorBinding As String
Get....
Set(value As String)
Dim b As New Binding(value)
b.Mode = BindingMode.TwoWay
b.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
Dim tb = DirectCast(Me.CellEditingTemplate.LoadContent, TextBox)
tb.SetBinding(TextBox.TextProperty, b)
End Set
End Property
Not working, my best guess is I'm not setting the Binding.Source, but I have no idea what I should be setting it to. It's getting pretty frustrating.
So if I understand you correctly, you want to be able to bind the text property of the TextBox to something on the parent control which will hold this child control of yours. You can't do that using the normal property (I'm guessing you got the "Can't bind because it's not the dependency property" exception or something similar).
This is how I usually do it without any problems. First you need to define a dependency property in the code behind. This should show you how to do it in the VB.net (I really really suck at VB.net so I won't pretend to give you any advice on that). Check the first example in VB.net. What you need to change first is from Boolean to String, you will also probably want to change the property name. Be careful to leave the "Property" part of the name where it stands in the example. GetType(MyCode) should be changed to the name of the class where you are implementing the dependency property (the name of your MyTextColumn class)
In the MyTextColumn xaml, it should look something like this:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Text="{Binding Path=IsSpinning, RelativeSource={RelativeSource AncestorType=DataGridTemplateColumn}, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"/>
I've put the original property name IsSpinning, you should put there your chosen name. Also, you might have to fix the relative source if the base class is not DataGridTemplateColumn. This should pick up anything comming to your custom control.
The final step is to use your control:
<controls:MyTextColumn IsSpinning="{binding PropName}"/>
You basically bind it to whatever string you want. Feel free to write up any problems that you might have with my explanation or code and I'll fix my answer accordingly.
I have a question regarding WPF DataGrid. For the sake of IDataErrorInfo validation I would like to set the entire selected row as editing - by that I mean setting every cell's (in that row) data template from CellTemplate to CellEditingTemplate.
This is one column for example:
<DataGridTemplateColumn Header="Note">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Name="textBoxNote" Text="{Binding Note, ValidatesOnDataErrors=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Note}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Is that possible in XAML (triggers of some kind)? How would I do that in codebehind? I have found the solution with two separate styles as resources and then programamatically toggling between them in Row_Selected and Row_Unselected events, but I would rather use the existing above XAML code for columns (with separate CellTemplate and CellEditingTemplate).
Can anyone point me the right way?
Thanks in advance. Best regards,
DB
Ok, I didn't manage to put the whole row into edit mode, but I managed to revalidate the IDataErrorInfo object - kind of forced IDataErrorInfo validation. This was the reason for me wanting to set edit mode on all cells of the row - to bind controls from CellEditingTemplate to object properties with ValidateOnDataErrors = True. Otherwise I added new object to the DataGrid, but properties (except of the edited ones) never got validated.
In the superclass of all of my model objects (that extends IDataErrorInfo) I added this method:
public virtual void Revalidate() // never needed to override though
{
Type type = this.GetType();
// "touch" all of the properties of the object - this calls the indexer that checks
// if property is valid and sets the object's Error property
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
var indexerProperty = this[propertyInfo.Name];
}
}
Now when the user adds new object to DataGrid I manually call myNewObject.Revalidate() method to set the Error property which I check before saving the object to the database.
Thanks and regards,
DB
I have a datagrid (A C1 datagrid, in this case) bound to a property in my View Model. The XAML for the datagrid looks like this:
<c1:C1DataGrid
AutoGenerateColumns="False"
IsReadOnly="False"
Margin="5" Width="auto"
MinWidth="250"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Name="dgNotifAssign"
CanUserAddRows="True"
ItemsSource="{Binding Path=notifCodeSubs.notification_configuration}"
>
<c1:C1DataGrid.Columns>
<c1:DataGridTextColumn
Binding="{Binding Path=user_id}"
Header="Recipient"
VerticalAlignment="Stretch"
SortMemberPath="user_id"
>
The property that it is bound to, in my viewmodel, looks like this:
Public Property notifCodeSubs As dsPeruseFM
Get
If _notifCode Is Nothing Then
_notifCode = New dsPeruseFM
End If
Return _notifCode
End Get
Set(ByVal value As dsPeruseFM)
MsgBox("If you can see this, success!")
End Set
End Property
In the codebehind I create an instance of the viewmodel and set the datacontext of the xaml to that instance, rather simple...
Dim vm As New ctrlAlertNotifyVM
As well as:
ctrlAlertNotifyXML.DataContext = vm
The above configuration compiles and reads data just fine. The grid is populated with all the correct data, etc. The problem comes when I try to add Mode=twoway to the ItemsSource on the datagrid. At that point VS2010 spits out the following error:
A TwoWay or OneWayToSource binding cannot work on the read-only property 'notification_configuration' of type 'PeruseFM.dsPeruseFM'.
I'm quite sure that all of my properties are read/write. And while the set command for this is nothing more than a message box at this point, it doesn't seem like I can even access that.
So the question is... has anybody ever encountered this issue before?
Update, response to question "What does notification_configuration look like?" from sixlettervariables:
Public Function codeChanged(Optional ByVal x As String = "")
If _notifCode Is Nothing Then
_notifCode = New dsPeruseFM
End If
taNotifSubs.fillNotifSubs(notifCode:=x, dataTable:=_notifCode.notification_configuration)
Return _notifCode
End Function
You've shown us that notifCodeSubs is read/write, however, that is not the actual property you've bound to.
From this viewpoint, the error message is fairly self-explanatory:
...read-only property 'notification_configuration'...
Therefore you cannot apply TwoWay binding to that property as an ItemsSource.
In the below window, the Existing Reports combo is bound to an observeablecollection of reportObjects. I have a reportObject property currentReport bound to the combo's SelectedValue property, OneWay. However, that's not working when bound in XAML.
SelectedValue="{Binding currentReport, Mode=OneWay}"
TwoWay binds fine, but I can't do it that way without writing an undo() method to the reportObject class. I'm binding the currentReport's properties to the various textboxes for editing. I want to bind OneWay so the source doesn't get changed. The currentReport's properties are all TwoWay bound to the corresponding textboxes so when I update the table in SQL [Save], it'll pull from that object, who's data is current.
<TextBox Text="{Binding currentReport.reportName, Mode=TwoWay}"
All of the properties bound from currentReport to the textboxes work fine as well. The only problem is the OneWay binding from the SelectedValue to the currentReport object. Does anyone have any ideas how to get this to work? I saw there was a bug, but the post I saw was 2009.
Sorry about the yellow. Not my idea. =)
EDIT: Added this XAML just in case.
<ComboBox ItemsSource="{Binding reportsCollection}" SelectionChanged="cboReports_SelectionChanged"
DisplayMemberPath="displayName"
SelectedValue="{Binding currentReport, Mode=TwoWay}"
x:Name="cboReports" Width="342" Height="40" VerticalAlignment="Center"/>
Forget about you need to change values - that is a separate problem - need to review your data design. Start with the UI problem question. If you want a user to be able to select an item from a combo box then it must have two way binding. Your first question is SelectedValue="{Binding currentReport, Mode=OneWay}" is failing why?
Is it possible?
I have a listview with several gridviewcolumns. The last column has a dynamic header. I dont know what the column header will be at design time. It's actually a number I want to display as a string.
<GridViewColumn Header="{Binding Path=SomeValue}"
DisplayMemberBinding="{Binding Path=OtherValue}"/>
This doesn't seem to work. The data will bind fine just the header remains blank. Stepping through the code and it doesn't even break on the SomeValue property.
I think your problem is the source of the "SomeValue" property. If you are binding to a list of objects, it wouldn't make sense to have the header determined by a property on that object, because then you could have a different header for every object. Essentially what you are saying is "Bind the header of the column to the 'SomeValue' property which lives on the same object that my 'OtherValue' property does." The "SomeValue" needs to come from a different source other than the list your grid item is bound to. You need to either set the "RelativeSource" or the "ElementName" property in the binding.
read a post
http://blogs.infragistics.com/blogs/josh_smith/archive/2008/06/26/data-binding-the-isvisible-property-of-contextualtabgroup.aspx
2.create a datacontext spy class accourding to the post in step 1 (copy and paste it, it's a couple of lines)
3.usage the datacontext spy
<common:DataContextSpy x:Key="dci" DataContext="{Binding SomeProperty}" />
<DataGridTemplateColumn Header="{Binding Source={StaticResource dci},
Path=DataContext.SomePropertysListOfValues[14]}">