I'm fairly new to Data binding & XAML, so this probably is fairly simple thing but I've been stumped on it for days now (and frustrated with more googling than i can track at this point) and would appreciate any pointers in the right direction. My only preference is to keep it in pure XAML if possible.
In my RIA SL4 project, I have two Entities PackageOS and OS where PackageOS has an association to OS through PackageOS.OS (associating through PackageOS.OSID <-> OS.ID - and [Include] + .Include() setup properly on relevant sections)
This is the template (defined in Page.Resource section along with all other involved DDS) I'm using in DataForm to get OSEntities List to bind into PackageOS Entity (coming from RIA GetOSEntities() using DDS):
<DataTemplate x:Key="POSItemTemplate">
<StackPanel>
<toolkit:DataField Label="PackageOS.OS">
<TextBlock Text="{Binding Source={StaticResource packageOSEntityDomainDataSource}, Path=Data.CurrentItem.OS}" />
</toolkit:DataField>
<toolkit:DataField Label="OS">
<ComboBox ItemsSource="{Binding Path=Data, Source={StaticResource osEntityDomainDataSource}}"
SelectedItem="{Binding Path=Data.CurrentItem.OS, Source={StaticResource packageOSEntityDomainDataSource}}"/>
</toolkit:DataField>
</StackPanel>
</DataTemplate>
The core problem is SelectedItem of ComboBox is not working. All the bindings are reachable from IDE Binding wizard so it's not a problem of me typing incorrect path. I can see packageOSEntityDomainDataSource.Data.CurrentItem to be of type PackageOS.
If i create a manual entry in backend database, the result is shown in PackageOS.OS textblock so I know it is properly being returned but SelectedItem refuses to pick it up (it ends up selecting the first value in dropdown list regardless of OS item in PackageOS).
Many thanks in advance!
Finally figured this out. Leaving my answer in hopes that it saves somebody else the time that I spent on this.
First Lesson
The problem was in the fact that I didn't have a custom Equality implementation for generated entities and default reference equality didn't work as I was using two different instances. Once I implemented IEquatable on my generated entities (through .shared.cs partial classes on server side) everything started working like a charm.
For details please see Silverlight ComboBox Control Population by Manishdalal
Second lesson
Do not use multiple DDS controls if you can help it. Especially once you use a write operation on a DDS, you cannot load/refresh any other DDS that is sharing the DomainContext until changes are committed. The link above shows how to avoid multiple DDS by using list generators when all you want is to pick up list of entities to fill ComboBox up.
My new code looks like this:
<DataTemplate x:Key="POSItemTemplate">
<StackPanel d:DataContext="{Binding Source=packageOSDomainDataSource, Path=Data.CurrentItem}">
<toolkit:DataField Label="OS">
<ComboBox DisplayMemberPath="Name"
ItemsSource="{Binding Path=OSList, Source={StaticResource OSListGenerator}}"
SelectedItem="{Binding Path=OS, Mode=TwoWay}" />
</toolkit:DataField>
</StackPanel>
</DataTemplate>
Where OSListGenerator is returning an IEnumerable<OSEntity> through its OSList property after loading it from DomainContext
Third Lesson
In DDS DataTemplate you have to be explicit with TwoWay Binding. This is the new behaviour; something that took me days to figure as most of the tutorials I referred to were using SL3 and I didn't realize that this was a breaking change in DDS DataTemplate behaviour in SL4.
Related
Is it possible to store some data in every item of a TreeView control? I mean, something useful (e.g. a string) besides the header text?
Thanks.
Yes, WPF is "lookless", so your actual data can be anything you want it to be, and a TreeView is just a Template used to display the data to the user in a pre-determined way.
You can overwrite any part of that Template to be whatever you want, and/or have it bind to your data however you want.
Edit
I'm no expert on using the TreeView, but if you had a DataContext of List<Folder>, and each Folder object had a Name and a FullPath property, your TreeView could look something like this:
<TreeView ItemsSource="{Binding MyFolderList}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
ToolTip="{Binding FullPath}" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
If you haven't already, I'd highly recommend looking into the MVVM design pattern when working with WPF. Basically your application is your classes (ViewModels), and the Controls/XAML (Views) are just a pretty layer that sits on top of your classes to make them user-friendly.
This is an important concept when switching from a WinForms TreeView to a WPF TreeView
It depends on what you mean by store data...
If you're just talking UI customization Rachel's answer above works.
If you're talking about storing arbitrary object values, such as information about the TreeViewItem, or maybe a relation between two items, you can use the Tag property of TreeViewItem. For example, I had to write a mapping UI where two trees linked together where each TreeViewItem from the first tree, could connect to 1 TreeViewItems of the second tree. I used the Tag property of the first TreeViewItem to store the connecting TreeViewItem.
first of all I would like to thank you for the many good posts that i read in this forum. Unluckily I could not find anything of help for my current problem (either here or anywhere else).
What I'm trying to do sounds quite simple, but I have no clue how to get it to work ... perhaps I'm still to new to wpf or I don't thing wpfy enough :)
I'm designing a front end for a part in an automated manufacturing:
I have a quantity of places where pallets can be put (but it can be empty as well).
Each pallet has up to 3 places where parts can be mounted
Everything is created dynamically of a database and is reacting to changes.
The position of the parts on the pallet comes from the database as well and should be visualized
What I would like to have is:
An overview over the pallet-places with a preview of the pallet
When I select a place I want to see a detail view of the place
When I click on a part on the pallet of the detailed pallet I want to see details to the part
The first two points are quite simple and work nicely:
I have got a DataTemplate for every component (part, pallet, pallet-place). Actually those are UserControls that are imported as Datatemplates
the overview is a ListBox with the places as DataContext
for the detail-place-view I use the UserControl and bound it to the SelectedItem of the Listbox
I tried to bind the Text of a Textblock to the ID of the selected Part ... and fail.
Probably I could use some global variables in the code behind - but that sound very ugly.
Can anybody help?
I have got a solution ... it is not nice but works.
I created an event in the pallet, that triggers, when the selected part-place changes
I handle the event in the pallet-place and create a new one
And finally I handle it in the overview and change the detailview accordingly
Most likely there are much nicer solutions, but it will suffice.
Perhaps try an ElementName binding?
<TextBlock Text="{Binding ElementName=Name_of_your_Listbox, Path=SelectedItem.ID" />
Can you post a bit more code of your TextBlock and your Binding?
Context is important, if i use a ContentControl and bind its content to the SelectedItem like this:
<ContentControl Content="{Binding SelectedItem, ElementName=mylistbox}">
I can bind to the ID of the selected item in the DataTemplate like this:
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ContentControl.ContentTemplate>
That is because setting the Content of the ContentControl automatically sets the DataContext as well, and this binding is relative to the DataContext since no source (ElementName, RelativeSource, Source) has been specified.
I do not know how your UserControl handles the context, if the DataContext is not affected such bindings will not work. You would need to bind directly then:
<uc:MyDetailsView Data="{Binding SelectedItem, ElementName=mylistbox}">
<!-- ... -->
<TextBlock Text="{Binding SelectedItem.ID, ElementName=mylistbox}" />
This of course defeats the purpose of having the binding on the UserControl itself in the first place. But unless you post some relevant code it's quite hard to tell what is wrong.
Also check the Output window in VisualStudio, binding errors will show up there and might provide valuable information as to what went wrong.
(see below for my own answer that I came up with after letting this percolate for days & days)
I am trying to achieve the following scenario in WPF.
I have a datagrid that is displaying rows of data for viewing and additional data entry. It is a new app but there is legacy data.
One particular field in the past has had data randomly entered into it. Now we want to limit that field's values to a particular list. So I'm using a DataGridComboBoxColumn. FWIW I have alternatively tried this with a DataGridTemplateColumn containing a ComboBox.
At runtime, if the existing value is not on the list, I want it to display anyway. I just cannot seem to get that to happen. While I have attempted a vast array of solutions (all failures) here is the one that is most logical as a starting point.
The list of values for the drop down are defined in a windows resource called "months".
<DataGridComboBoxColumn x:Name="frequencyCombo" MinWidth="100" Header="Frequency"
ItemsSource="{Binding Source={StaticResource months}}"
SelectedValueBinding="{Binding Path=Frequency,UpdateSourceTrigger=PropertyChanged}">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
What is happening is that if a value is not on the list then the display is blank. I have verified at runtime that the IsSynchronizedWithCurrentItem element is indeed False. It is just not doing what I am expecting.
Perhaps I am just going down the wrong path here. Maybe I need to use a textbox in combination with the combobox. Maybe I need to write some code, not just XAML. I have spent hours trying different things and would be really appreciative of a solution. I have had a few suggestions to use this class or that control but without explanation of how to use it.
Thanks a bunch!
I have finally solved this.
The trick is to get rid of the comboboxcolumn and use a template that has a textbox for display and a combobox for editing. However, I still spent hours with a new problem...when making a selection in the combobox, it would modify any other rows where I had also used the combobox in the grid. Guess what solved the problem! The IsSynchronizedWithCurrentItem property that I was trying to use before. :)
<DataGridTemplateColumn x:Name="frequencyCombo" Header="Frequency">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Frequency}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox
ItemsSource="{Binding Source={StaticResource frequencyViewSource},
TargetNullValue=''}"
SelectedItem="{Binding Path=Frequency}" IsSynchronizedWithCurrentItem="False"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
No ugly hacks. No non-usable choices hanging around at the bottom of the dropdown. No code to add those extra values and then clean them up.
I am not going to take away the "Answer" on Mark's suggestion since it enabled me to get the app into my client's hands, but this is the solution I was looking for. I found it buried in a "connect" item after hours of web searching.
Thanks for everyones help!
Could you please clarify what's happening here? It's unclear what the "existing value" is at runtime - if this is the field where data is being randomly entered, by limiting it does this mean you're running some sort of validation logic although you still want it to display?
Also, I'm more on the Silverlight side of things...does WPF also default to one way binding?
Rather than mixing the static resource and the view model property as a source for items on the list, have you tried using an ObservableCollection or CollectionViewSource in the view model? Then you could insert and remove the non-standard items at will and make them selected (or not) whenever you want. So the "normal" list would have the normal months, but when an odd one comes along, add that to the list and make it selected. Seems like it would be easier to control exclusively in the view model.
Why not just do something like:
//create collection
PagedCollectionView view = new PagedCollectionView(e.Result);
view.SortDescriptions.Add(
new SortDescription("Months", ListSortDirection.Ascending));
gridProducts.ItemsSource = view;
//filter collection by category
PagedCollectionView view = new PagedCollectionView(e.Result);
view.Filter = delegate(object filterObject)
{
Product product = (Product)filterObject;
return (product.CategoryName == "Legacy");
};
gridProducts.ItemsSource = view;
//create categories through grouping
PagedCollectionView view = new PagedCollectionView(e.Result);
view.GroupDescriptions.Add(new PropertyGroupDescription("Legacy"));
view.GroupDescriptions.Add(new PropertyGroupDescription("etc..."));
gridProducts.ItemsSource = view;
Try this:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Months}"
Text="{Binding Value}"
IsEditable="True" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
How can I recursivly bind a Treeview to an XDocument, mapping each XML Element to a Node in the Treeview?
The code below should work from my perspective (and also according to the very few posts I found regarding direct binding), however it does not:
<sdk:TreeView ItemsSource="{Binding Path=Elements}" DataContext="{Binding Path=Data}">
<sdk:TreeView.ItemTemplate>
<data:HierarchicalDataTemplate ItemsSource="{Binding Path=Elements}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</data:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:Treeview>
(Data is a Property of type XElement on the parents' DataContext)
Did I make a mistake somewhere or do I really need to implement an IValueConverter just to get at the child elements of an XElement?
The "Elements" member is not a Property, It's a Method call.
You cannot bind to method calls in Silverlight.
If you're really bent on getting this scenario to work you've got 2 options I can see:
1. Use an IValueConverter to extract the contents of the "Elements" method.
2. Wrap the XDocument in managed classes in a proper hierarchy.
Personally, While option #1 seems the fastest, I believe that in the long run it'll cost you more time to maintain and support then spending an additional 10 minutes building a proper domain model.
Sincerely,
-- Justin Angel
I have two WPF list boxes. One is a list of lists (actually a List of ObservableCollection), the other is a list of all known instances of "Thingy".
Here's the datatemplate I'm using for the "thingy" class.
<DataTemplate DataType="{x:Type Model:Thingy}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="ThingyInListCheckBox" Click="ThingyInList_Click"></CheckBox>
<TextBlock Text="{Binding Path=ThingyName}"></TextBlock>
</StackPanel>
Here's the XAML for the list boxes:
<ListBox
Name="ListOfGroups"
SelectionMode="Single">
</ListBox>
<ListBox
Name="ListOfThingys"
SelectionMode="Single">
</ListBox>
I have the data binding for the list boxes set up in code, because I'm too tired to figure out how to do it in XAML:
ListOfGroups.ItemsSource = InMemoryCache.ThingyGroups;
ListOfThingys.ItemsSource = InMemoryCache.Thingys;
What I want is the checkbox "ThingyInListCheckBox" to be checked if the 'thingy' object is in the list that is the selected item in the "ListOfGroups" listbox. So basically I need to bind it to the "Contains" method of the "ListOfGroups".SelectedItem while passing it the "ListOfThingys".SelectedItem as a parameter.
I'm tempted to do this all in code, but I'm trying to get a better understanding of XAML data binding because I hate myself and I want me to suffer.
Is this even possible, or have I hit the inevitable "wall of databinding" that exists in every other data binding system in the history of software development?
It is possible, in fact the hard thing is that there are many ways to do this and you have to choose one. None of them is a simple addition to your current code. However there is one way, by which you gain more than solving your problem. Actually, it is more of a pattern, called MVVM (some might argue about the naming).
Here is a small explanation on your example.
Suppose ThingyGroup has an IsSelected property, which is bound to the IsSelected property of the containing ListBoxItem. Again, suppose Thingy has a Group property too. Then you can use Group.IsSelected as a path to bind checkbox. Notice that there is still a small issue that IsSelected is a bool and IsChecked is a nullable bool.
A search on MVVM should give you concrete samples.