Adding an x:Name attribute to a XAML element normally results in a member variable being added to the backing class that can then be accessed using normal code. When the element in question is part of the DataTemplate, the field does not get created.
I can sort of understand that the DataTemplate is making this a special case but can anyone explain the underlying principle to me? Also what are the options for getting access to the object within .NET Code?
<dataControls:DataForm x:Name="CompanyDetail" CurrentItem="{Binding CurrentItem}" AutoGenerateFields="False">
<dataControls:DataForm.EditTemplate>
<DataTemplate>
<StackPanel dataControls:DataField.IsFieldGroup="True">
<dataControls:DataField Label="About">
<Border Height="150" Style="{StaticResource HtmlPlaceHolderBorderStyle}" Width="298" VerticalAlignment="Top">
<telerik:RadHtmlPlaceholder x:Name="uxAboutHtml" x:FieldModifier="Public" HtmlSource="{Binding About, Mode=TwoWay}"/>
</Border>
</dataControls:DataField>
</StackPanel>
</DataTemplate>
</dataControls:DataForm.EditTemplate>
</dataControls:DataForm>
You can use the FrameworkElement.FindName("objectName") method on the parent of the DataTemplate e.g. var uxAboutHtml = CompanyDetail.FindName("uxAboutHtml"); to get a reference to the object. The downside to this is that paramater passed to FindName does not end up being strongly typed with the XAML x:Name"objectName" attribute.
I have changed tack on this and am now referencing the underlying object that the control is being bound to, which is probably a better way to go.
var htmlContent = (CompanyViewModel)CompanyDetail.CurrentItem;
Related
Knowing a HierarchicalDataTemplate is defined somewhere in the resource tree (i.e. it could be defined at the app level, in a style, in the window's resources or somewhere in the hierarchy of controls on that window), programmatically, how can you determine which HierarchicalDataTemplate will be applied for a particular data type relative to a specific control?
For instance, in the following example, given that we have an object of type Foo, how can we get the HierarchicalDataTemplate for it--which happens to be defined at the window level here--relative to MainTreeView?
<Window ... >
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type Foo}"
ItemsSource="{Binding Children}">
<TextBlock Text={Binding Name}" />
</HierarchicalDataTemplate>
</Window.Resources>
<TreeView x:Name="MainTreeView" />
</Window>
I've tried the following but it returns null:
var hdt = (HierarchicalDataTemplate)MainTreeView.FindResource(typeof(Foo));
Found it. DataTemplate objects don't use DataType for their default key like Style objects do. Instead, they use a DataTemplateKey object which you get as follows...
var dataTemplateKey = new DataTemplateKey(dataItem.GetType());
var hdt = (HierarchicalDataTemplate)MainTreeView.TryFindResource(dataTemplateKey);
That worked! :)
I create a datatemplate for a combobox as follows:
<DataTemplate x:Key="AircraftTypeTemplate">
<StackPanel Orientation="Horizontal" Width="340">
<ComboBox>
<ComboBoxItem>CJ1</ComboBoxItem>
<ComboBoxItem>CJ3</ComboBoxItem>
<ComboBoxItem>Bravo</ComboBoxItem>
<ComboBoxItem>Excel</ComboBoxItem>
<ComboBoxItem>Sovereign</ComboBoxItem>
</ComboBox>
</StackPanel>
</DataTemplate>
It renders fine, but I would like to be able to associate a value with each of the items without having to bind it to some data context. For example I would like the CJ1 comboboxitem to have a value of 5. How would I set those in XAML?
Like:
<ComboBoxItem Value="5">CJ1</ComboBoxItem>
Thanks!
You can set the Name property to be any arbitrary string and use that. For more flexibility, you can use the Tag property, which according to MSDN:
Gets or sets an arbitrary object value that can be used to store custom information about this object.
You can read more about Tag here. I'd say Tag is probably better as opposed to bending Name to your will, and you can stick a string into Tag just as easily as Name.
In my WPF project, I have a ListBox that displays items from a List<string> collection. I wanted to make the text of these items editable, so I wrapped each of them in an ItemTemplate with a TextBox (might not be the best way, but I'm new to WPF). I was having trouble simply binding the TextBoxes' Text property to the value of each item. I finally stumbled upon an example using a single dot or period for its Path property ({Binding Path=.}):
<ListBox ItemsSource="{Binding ElementName=recipesListbox,Path=SelectedItem.Steps}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=.}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
However I don't understand why simply using {Binding} didn't work.
It raised a "Two-way binding requires Path or XPath" exception, as according to Microsoft:
[...] a period (.) path can be used to bind to the current source. For example, Text="{Binding}" is equivalent to Text="{Binding Path=.}"
Could someone shed light on this ambiguous behavior?
EDIT: Moreover, it seems {Binding Path=.} does not necessarily give two-way binding, as modifying the text and moving the focus does not update the underlying source (the same source has also properties displayed and successfully modified on a DataGrid control). I'm definitely missing something here.
The point of the exception presumably is that you cannot two-way bind a binding-source itself, so it tries to prevent you from creating a binding which does not behave the way you would want it to. By using {Binding Path=.} you just trick the error detection.
(Also it's not unheard of that documentation is erroneous or inaccurate, though i do like the MSDN documentation a lot in general as it usually does contain the crucial points one is interested in)
The documentation states that {Binding} is equivalent to {Binding Path=.}. However it is not equivalent to {Binding Path} as you have typed. If you include the Path property, you must assign it to something, be it Path=. or Path=OtherProperty.
These are not the same. If you bind this where ConsoleMessages is an ObservableCollection string with just {Binding} you get a "Two-way binding requires Path or XPath." exception where as {Binding Path=.} works. This is with WPF 4.0...
<ItemsControl x:Name="ConsoleOutput" ItemsSource="{Binding ConsoleMessages, Mode=OneWay}" MaxHeight="400">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=.}" BorderThickness="0" Margin="0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
My 2p worth...
In short, the difference between the two is analogous with the difference between the traditional pass by value and pass by reference. (FYR - What's the difference between passing by reference vs. passing by value?)
However I don't understand why simply using {Binding} didn't work (it raised a "Two-way binding requires Path or XPath" exception)
Lets assume here for now that {Binding} can be used for two way binding. In general {Binding} creates a value based link with datacontext which does not allow updating the datacontext.
Whereas {Binding Path=.} creates reference based link with the memory area referenced by the 'Path' which allows updating the value through reference.(in this case 'dot' the current datacontext).
Hope this helps!
I have an ObservableCollection of addresses that I am binding to a ListBox. Then in the ItemTemplate I am Binding to the current address record using {Binding .}. This results in my addresses displaying using their ToString method which I have setup to format the address. All is good, except if I update properties on an individual address record the list in the UI does not update. Adds/Deletes to the list do update the UI (using the ObservableCollection behavior). If I bind directly to properties on the address the UI does update (using the INotifyPropertyChanged behavior of the Address object).
My question is, is there a way to notify the UI of the change to the object as a whole so that I can still use this syntax or do I need to punt and put a DisplayText property on my address type that calls the ToString method and bind to that? FYI, this is an MVVM architecture so I don't have the luxury of calling Refresh on the ListBox directly.
Thanks for any help/ideas.
<ListBox x:Name="AddressList" ItemsSource="{Binding Addresses}" Background="Transparent" BorderBrush="Transparent"
Width="200" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding .}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you bind to the Address object itself, the object itself -- that is, its identity -- doesn't change, even though its properties do. WPF therefore doesn't know to refresh the binding in this case.
So yes, you need to bind to a notifying property (or properties) rather than the whole object. As you say, one way to do this is to create a DisplayText property, and raise the PropertyChanged event for that property whenever something that affects the display text changes. Another is to use multiple TextBlocks in a horizontally oriented StackPanel, each bound to a particular property e.g.
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HouseNumber}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Street}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding City}" />
</StackPanel>
The advantage of the second approach is that it gives you flexibility in the UI to change how addresses are displayed, e.g. multiple lines, formatting, etc.; the downside is that it gets complicated if you have conditional logic e.g. an optional flat number or second address line.
I tried to reproduce the problem and succeeded.
I activated the step-into-.NET debugging options, and saw that WPF does not listen to INotifyPropertyChanged if the path in the binding is empty.
What worked to get a change to be reflected in the list box is to replace the whole object in the ObservableCollection. This triggers the INotifyCollectionChanged, with the Replace action.
But this may not be acceptable in your case. And it could be seen more like a hack than a solid solution.
I'd seriously consider having a DataTemplate for Address. There you should bind to the exact properties you need (which would create the listener for INotifyPropertyChanged). It is more flexible than ToString() and you may encounter cases where you have a need for ToString() to do something for non-UI stuff, which would create a conflict. And honestly, ToString is not really meant for UI stuff.
I have the following code and basically what i am not able to figure out is how to clone the whole grid and make a blank copy of them side by side.... for a clear understanding this is something to do with hospital application and the grid is related to a pregnancy so when said 'ADD CHILD' button a whole new grid should be created during run time, thanks for the help below is a link that might help people cause i tried it but not sure how to display it
How can you clone a WPF object?
You should put the object you are want to "clone" in a DataTemplate and reference this template from an ItemsControl, then when you need another grid add another item to the items control (or even better to the list the control is bound to) and the ItemsControl will create a new grid and bind it the new object.
For an example take a look at this post on my blog.
Here is an example for this application (I left only the relevant parts and I didn't test it, so there are probably some typos there):
<Window ... >
<Window.Resources>
<DataTemplate x:Key="ChildTemplate">
<Grid>
...
<TextBlock Text="Delivery Date:" Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding DeliveryDate}" Grid.Column="1" Grid.Row="0"/>
<TextBlock Text="Delivery Time:" Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding DeliveryTime}" Grid.Column="1" Grid.Row="1"/>
...
</Grid>
</DataTemplate>
</Window.Resources>
...
<Button Content="AddChild" Click="AddChildClick"/>
...
<ScrollViewer>
<ItemsControl ItemsSource="{Binding AllChildren}" ItemsTemplate="{StaticResource ChildTemplate}">
<ItemsControl.PanelTemplate>
<ItemsPanelTemplate><StackPanel Orientation="Horizontal"/></ItemPanelTemplate>
<ItemsControl.PanelTemplate>
</ScrollViewer>
...
</Window>
And in cs:
Set an object with all the form data as the Window's DataContext. I'll call this class PostDelveryData.
Create another class with the repeating data. I'll call it ChildDeliveryData.
Add a property of type ObservableCollection<ChildDeliveryData> called AllChildren to PostDeliveryData; it's important it'll be ObservableCollection and not any other type of collection.
Now, for the magic:
private void AddChildClick(object sender, RoutedEvetnArgs e)
{
((PostDeliveryData)DataContext).AllChildren.Add(new ChildDeliveryData());
}
And when you add the new item to the list another copy of the entire data template will be added.
I'm not sure that you're using the correct approach here. I would approach the problem by creating a "ChildGridControl" with a Child property, and let the Child property handle the databinding. Adding a new child to the GUI would involve creating a new instance of the ChildGridControl.
If I am understanding correctly, you should create a UserControl, which wraps your Grid and subsequent controls inside. And use this User control anywhere you wanted to replicate that UI.