WPF - Create Treeview - wpf

I have list of type field that can represent a hierarchy: List MyFields
public class Field
{
public Field(string name, string value)
{
this.Name = name;
this.Value = value;
}
public string Name { get; set; }
public string Value { get; set; }
public IList<Field> SubFields { get; set; }
}
How can i bind MyFields to a TreeView?
EDIT:
I forgot, i want to eg. show the value in a message box when clicking on the item.

Set the TreeViews ItemsSource to the Property you want to bind with.

You can create a HierarchicalDataTemplate which should be placed in the resources of your TreeView or at an higher level, make sure to set the DataType to your class to make it apply.
Something like this for example:
<HierarchicalDataTemplate DataType="{x:Type data:Field}"
ItemsSource="{Binding SubFields}">
<ContentControl MouseDoubleClick="TreeViewItem_MouseDoubleClick">
<TextBlock Text="{Binding Name}"/>
</ContentControl>
</HierarchicalDataTemplate>
private void TreeViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
Field field = (sender as FrameworkElement).DataContext as Field;
MessageBox.Show(field.Value.ToString());
}
You also need a root-elements list to which you can bind the ItemsSource of the TreeView itself.

Related

WPF Combo Box not displaying dynamically changed selected item

I am trying to change the selected item of a combo box based on a change in another combo box. The situation is complicated by the fact that both combo boxes appear in a list of item templates.
The XAML is as follows:
<ListBox ItemsSource="{Binding AncillaryExtendedPropertyViewModels}" ItemTemplateSelector="{StaticResource templateSelector}"/>
<DataTemplate x:Key="EnumDataTemplate"> <Grid Margin="4"
MinHeight="25"> <ComboBox SelectedItem="{Binding ExtendedPropertyEnum,
UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding ExtendedPropertyEnumList}"
DisplayMemberPath="Value"/> </Grid> </DataTemplate>
The data context of the view containing the XAML is set to AncillaryBaseViewModel. The following is a cut down version of AncillaryBaseViewModel.
public class AncillaryBaseViewModel : ComplexOrderItemViewModel, IDataErrorInfo
{
private ObservableCollection<ExtendedPropertyViewModel> _ancillaryExtendedPropertyViewModels;
public ObservableCollection<ExtendedPropertyViewModel> AncillaryExtendedPropertyViewModels
{
get { return _ancillaryExtendedPropertyViewModels; }
set
{
_ancillaryExtendedPropertyViewModels = value;
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
}
and the ExtendedPropertyViewModel class....
public class ExtendedPropertyViewModel : DataTemplateSelector
{
private ExtendedProperty _extendedProperty;
public DataTemplate DefaultnDataTemplate { get; set; }
public DataTemplate BooleanDataTemplate { get; set; }
public DataTemplate EnumDataTemplate { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{ return ExtendedProperty.ExtendedPropertyEnum; }
set
{
if (ExtendedProperty.ExtendedPropertyEnum != value)
{
_extendedProperty.ExtendedPropertyEnum = value;
AncillaryBaseViewModel parent = RequestParent();
if (parent != null)
{
parent.AncillaryExtendedPropertyViewModels[7].ExtendedPropertyEnum =
ExtendedProperty.ExtendedPropertyEnum.DependentExtendedPropertyEnums[0];
}
parent.OrderItem.SetStockCode();
PropertyChanged += parent.OnExtendedPropertyChanged;
OnPropertyChanged();
}
}
}
and the ExtendedProperty class....
public class ExtendedProperty : ViewModelBase
{
private ExtendedPropertyEnum _extendedPropertyEnum;
public int ExtendedPropertyID { get; set; }
public int OrderItemTypeID { get; set; }
public string DisplayName {get; set;}
public ObservableCollection<ExtendedPropertyEnum> ExtendedPropertyEnumList { get; set; }
public string Value { get; set; }
public ExtendedPropertyEnum ExtendedPropertyEnum
{
get
{
return _extendedPropertyEnum;
}
set
{
_extendedPropertyEnum = value;
OnPropertyChanged("ExtendedPropertyEnum");
}
}
}
To summarise, when I change the value of combo box A through the UI, this calls the ExtendedPropertyEnum setter within ExtendedPropertyViewModel, which changes the ExtendedPropertyEnum bound to another combo box B, which is in the same list. I would expect this to update the value displayed in combo box B accordingly, which it does not.
As an aside, changing the value of combo box A does update a label that is not within a data template. The XAML for this label is....
<Label Content="{Binding StockCode}" MinWidth="100"/>
This is updated by the following code within AncillaryBaseViewModel....
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
}
I thought I could change this to the following to force the combo boxes in the list to update.
public void OnExtendedPropertyChanged(object sender, EventArgs args)
{
OnPropertyChanged("StockCode");
OnPropertyChanged("AncillaryExtendedPropertyViewModels");
}
However, this did not work either.
Any help greatly appreciated.
Roger.
if I understand the question correctly then you are expecting a changed value within an observablecollection to be reflected within the UI. This will not happen. observablecollections raise notifyproperty events when the collection itself changes, not when values within them change. You'll either need to raise a notifyproperty event on the value changing or remove the item from the list and add it back with a new value.

Silverlight combobox dropdown text/value

Is there anyway in Silverlight combobox to specify a value and a text like an html dropdownlist? I only see a content property. I read that I could use the tag property but I am not able to retrieve the value from the code behind when doing...
mycombobox.Tag.toString();
Anyone know of the best way to do this?
Thanks,
Have a custom class that can hold 2 properties to represent the value and text fields of a drop down item. And bind the list of that class as the ItemsSource of the combobox.
you could try
public class CustomComboBoxItem
{
public int Key { get; set; }
public string DisplayText { get; set; }
}
and
public MainPage()
{
InitializeComponent();
myComboboxSource = new List<CustomComboBoxItem>();
myComboboxSource.Add(new CustomComboBoxItem { Key = 1, DisplayText = "First Text" });
myComboboxSource.Add(new CustomComboBoxItem { Key = 2, DisplayText = "Second Text" });
}
public List<CustomComboBoxItem> myComboboxSource { get; set; }
In Xaml,
<ComboBox Name="myCombobox" Height="25" Width="200" ItemsSource="{Binding myComboboxSource, ElementName= mainPage}" SelectedValuePath="Key" DisplayMemberPath="DisplayText"/>
<Button Click="Button_Click" Height="25" Width="100" Content="Get Selected Value"/>
You can test the selected value by using
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(Convert.ToString(myCombobox.SelectedValue));
}

WPF DataGrid cell bound to Property of Property in domain object not updating

I've tried to get at this problem from a few angles. Here I've tried to simplify it into a small test case.
I'm having problems getting a DataGrid cell to update which is bound to a property of a property. The property is set by a bound ComboBox cell in another column. The bound object is a follows, with the property I'm referring to:
public class MainObject : INotifyPropertyChanged
{
private int _subObjectId;
public virtual SubObject SubObjectObj { get; set; }
public int SubObjectId {
get { return _subObjectId; }
set { _subObjectId = value; SubObjectObj = <GetObjFromDB> };
}
...
}
public class SubObject : INotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
public string Specialty{ get; set; }
...
}
The DataGrid ItemsSource is
public ObservableCollection<MainObject> SourceData;
Now, the column in the DataGrid is a ComboBox of SubObject choices. A TextBox column next to it which is (supposed) to display the SubObject.Specialty of whatever SubObject is selected in the ComboBox.
<DataGridTemplateColumn Header="SubObjects">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding SubObject.Name, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="ComboBoxSubObject" ItemsSource="{Binding Model.SubObjects, RelativeSource={RelativeSource AncestorType={x:Type uch:TestControl}}}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
SelectedValue="{Binding SubObjectId, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="ComboBoxDoctor_OnSelectionChanged"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Specialty" Binding="{Binding Path=SubObjectObj.Specialty}"/>
When the grid is initially painted, the Specialty column is correct - it's the property is what SubObject is displayed in the other column. But when I change the ComboBox, the Specialty column does not change. Is there anyway to tell the DataGrid that the Specialty column binding source has changed and to refresh?
Thanks for any advice.
Is there anyway to tell the DataGrid that the Specialty column binding
source has changed and to refresh?
Yes, this is where your INotifyPropertyChanged implementation comes into play. You should have an OnPropertyChanged event as part of that implementation, invoking this event with a property name tells WPF that the property value has changed and to update the UI. You should call OnPropertyChanged for the Speciality property when your SubObject changes. Because they're in different classes, you'll probably need to expose a method or an event to do this:
public class SubObject : INotifyPropertyChanged
{
public int Id { get; set; }
public string Name { get; set; }
public string Specialty{ get; set; }
public void OnSpecialityChanged()
{
OnPropertyChanged("Speciality");
}
}
public class MainObject : INotifyPropertyChanged
{
private int _subObjectId;
public virtual SubObject SubObjectObj { get; set; }
public int SubObjectId
{
get { return _subObjectId; }
set
{
_subObjectId = value;
SubObjectObj = <GetObjFromDB>
SubObjectObj.OnSpecialityChanged();
}
}
}
Side point, I'm unsure of what use your SubObjectId property is serving here. Could you instead maybe use the Id property directly from the SubObjectObj?

Silverlight: binding object collection to datatemplate in itemscontrols binds empty objects to property

Let me explain my situation. I have made a user control that contains an ItemsControl
<ItemsControl Name="itemControlReviewTags">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<my:ReviewControl ReviewEvent="{Binding}" />
<TextBlock Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This ItemsControl is bound to an observablecollection in the code behind
public ObservableCollection<TagEvent> tagItems = new ObservableCollection<TagEvent>();
The collection is set on the ItemsControl like so
itemControlReviewTags.ItemsSource = tagItems;
The TagEvent class is defined like below. The class is added to the collection at certain events.
public class TagEvent : EventArgs
{
public string Text { get; set; }
public string Comment { get; set; }
public string Value { get; set; }
public DateTime Time { get; set; }
public string Type { get; set; }
}
The ReviewControl in the datatemplate had a DependencyProperty like so
public TagEvent ReviewEvent
{
get
{
return (TagEvent)GetValue(ReviewEventProperty);
}
set
{
SetValue(ReviewEventProperty, value);
}
}
public static readonly DependencyProperty ReviewEventProperty = DependencyProperty.Register("ReviewEvent", typeof(TagEvent), typeof(ReviewControl), new PropertyMetadata(new TagEvent() { Comment = "hallo", Text = "De tag", Time = DateTime.Now, Type = "Mark", Value = "Mark" }, ReviewEvent_PropertyChangedCallback));
private static void ReviewEvent_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ReviewControl reviewControl = (ReviewControl)d;
reviewControl.LoadReviewEvent();
}
The strange thing is that when I run my progran the TagEvents get added to the collection and the ItemsControl shows the datatemplate for every item. The testblock I used to verify the value of the properties on the TagEvents shows the proper value of the text property. But in the ReviewEvent Dependency property I get only "empty" objects (all values empty string or default date). Those objects replace the default value as i can see that as the oldvalue in the DP callback.
I could understand the ItemsControl not showing the items, but why it is showing item that look like it's doing "new TagEvent" for every item in the collection is beyond me. Hope someone here has a suggestion for me. I tried implementing INotifyPropertyChanged on the TagEvent, but that did not seem to change anything. I could split out the properties of the TagEvent class but I don't see why I would have to do that, when I could pass the object.
Help?
I can now answer my own question. I had an statement setting the usercontrol's datacontext in code-behind that I forgot about. It messed things up.
Never leave old code laying about...

Child item in TreeView not updating

I copied the basic method of having checkbox in a treeview from the official Silverlight toolkit Checkboxes in a TreeView example.
When a user clicks on a parent TreeViewItem I want all of the child items to be checked, as in the above example. This works fine when the parent is collapsed, clicking the checkbox puts a tick in the parent and when you expand the node all children have a tick in the checkbox.
However it doesn't work if the parent is expanded. None of the children are updated to have a tick in the checkbox, although the underlying data list is updated.
My XAML is as follows:
<sdk:HierarchicalDataTemplate x:Key="NodeTemplate" ItemsSource="{Binding Path=Contracts}">
<StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{Binding Path=Name}">
<CheckBox IsTabStop="False" IsThreeState="{Binding Path=HasContracts}" IsChecked="{Binding Path=Selected, Mode=TwoWay}" Click="CheckBox_Click" />
<TextBlock Text="{Binding Path=Name}" Tag="{Binding Path=ID}"/>
</StackPanel>
</sdk:HierarchicalDataTemplate>
<sdk:TreeView x:Name="tvClientContract" ItemsSource="{Binding Path=ClientContracts, Mode=TwoWay}" ItemTemplate="{StaticResource NodeTemplate}"/>
This is bound to a List<ClientContract> and uses the same code behind as in the linked example.
The ClientContract object is:
public int ID { get; set; }
public string Name { get; set; }
public List<ClientContract> Contracts { get; set; }
public bool? Selected { get; set; }
How can I force the child to repaint itself as the underlying List<ClientContract> object is updated?
If you want to use INotifyPropertyChange(what I did instead of using ObservableCollection) here is how you do it per example on the ID element:
public class myclass : INotifyPropertyChanged
{
private int id_Value;
public int ID
{
get { return id_Value; }
set
{
id_Value = value;
NotifyPropertyChanged("ID");
}
}
public string Name { get; set; }
public List<ClientContract> Contracts { get; set; }
public bool? Selected { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I hope this helps if it was what you were trying to do.
Try using ObservableCollection<ClientContract> instead of a List<>. Usually you want to databind to this collection type instead when the data is dynamic so it can notify the UI of collection changes.

Resources