wpf treeview selected item - wpf

I have a treeview:
<TreeView>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=TucActivity}">
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="DisplayedStartTime"></Binding>
<Binding Path="Name"></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Message}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
bounded to Observable Collection object:
MainTreeView.ItemsSource = ((App)Application.Current).TucOC;
I want that every time the ((App)Application.Current).TucOC is updated
the selected item (and also the focus) will be the one in the observable collection.
I would like to do it in one place since the ((App)Application.Current).TucOC is
updated in multiple places in the code.
What's the best option to do it?

If you're using a development pattern like MVVM, I would create a property on your ViewModel class that's of the type held in the ObservableCollection, to hold the currently selected item for your treeview source. That would look something like this:
private object _selectedTuc;
public object SelectedTuc
{
get
{
return _selectedTuc;
}
set
{
_selectedTuc = value;
OnPropertyChanged("SelectedTuc");
}
}
Then, in your treeview, you bind this property to the treeview's SelectedItem:
<TreeView ItemsSource="{Binding TucOC, Mode=OneWay}" SelectedItem="{Binding SelectedTuc, Mode=TwoWay}">...</TreeView>
Notice on the binding for SelectedItem you specify a Mode value of TwoWay - this allows for your SelectedTuc property to be updated from the UI, as well as the UI being updated whenever the SelectedTuc property changes.
If you're not using MVVM or something like it, you're going to need to create a utility method that will update the TreeView's SelectedItem every time the selected item or index in your ObservableCollection changes. This is not, however, the way I would recommend doing it.

Related

Send current Item and checkbox value in command parameters

I have a TreeView setup with a HierarchialDataTemplate. It's ItemsSource is bound to a collection of Overlay objects in my viewmodel, where each Overlay has a collection of Layer objects (thus the HierarchialDataTemplate). For each Overlay, I'm displaying a CheckBox and a Label which is simply bound to the Overlay's Name property.
What I'm trying to do is, each time one of the checkboxes is checked/unchecked, the current Overlay and the IsChecked property of the CheckBox will be sent as command parameters to my viewmodel.
If I'm not using the MultiValueConverter, I can send one of the properties fine. But I need to send both as parameters.
Below is the related .xaml for the treeview. I'm only showing the necessary parts and just the Checked trigger because the Unchecked is exactly the same:
<TreeView ItemsSource="{Binding OverlaysViewSource}" Name="LayersTreeView">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Layers}" >
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding DataContext.SetVisibilityCmd, RelativeSource={RelativeSource AncestorType=UserControl}}" >
<i:InvokeCommandAction.CommandParameter>
<MultiBinding Converter="{StaticResource multiValueConverter}">
<Binding Path="IsChecked, RelativeSource={RelativeSource AncestorType=CheckBox}" />
<Binding/>
</MultiBinding>
</i:InvokeCommandAction.CommandParameter>
</i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
<Label Content="{Binding Name}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
So in the MultiBinding, the first one: <Binding Path="IsChecked, RelativeSource={RelativeSource AncestorType=CheckBox}" /> to try and send the checkbox's IsChecked property. However, the value I'm getting in the command is DependencyProperty.UnsetValue.
The second one is just for the current Overlay item, but the whole TreeView is being sent as a parameter.
Update:
The Overlay class is a third party control and is used in a lot of places that I can't modify. So I can't just add a property to it.
Update2: I've managed to get the Overlay to send properly. Just need the IsChecked property now.
The binding for IsChecked should use {RelativeSource Self}, since the binding is being applied to the CheckBox via the Style.
Your update to your question shows you've already solved the other one.

Display selected combobox item according to current item in parent datacontext

I have a wpf app that displays a Label in read-only mode and a ComboBox in edit mode. When the user clicks the 'Edit' button, the label is hidden and the ComboBox becomes visible (using a BooleanToVisibilityConverter).
The ItemsSource of the ComboBox is set to its lookup table (via auditorsViewSource), and the DataContext of the TextBox is set to the parent table (via auditStatementsViewSource).
Here is my xaml:
<!-- Auditor info -->
<StackPanel Orientation="Horizontal" DataContext="{StaticResource auditStatementsViewSource}">
<StackPanel Orientation="Horizontal" >
<Label Content="Auditor:" Name="lblAuditor" />
<!-- readonly data -->
<StackPanel Style="{StaticResource VisibleWhenReadOnly}">
<TextBox Name="txtAuditor" >
<TextBox.Text>
<MultiBinding StringFormat="{}{0} {1}">
<Binding Path="Auditor.GivenName"/>
<Binding Path="Auditor.Surname"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
<!-- editable data -->
<StackPanel Style="{StaticResource CollapsedWhenReadOnly}" >
<ComboBox x:Name="cboAuditor" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Surname"
ItemsSource="{Binding Source={StaticResource auditorsViewSource}}"
SelectedItem="{Binding Source=txtAuditor, Path=Auditor.Surname, Mode=TwoWay}" />
</StackPanel>
</StackPanel>
</StackPanel>
The ComboBox displays the items from the base (auditors) table correctly, but displays the first record in the table (according to the ORDER BY clause). How do I get it to initially show the item from the current (auditStatements) record, (ie, the Auditor that was being displayed by the readonly label), preferably in xaml?
Normally, Binding to the ComboBox.SelectedItem property works best if you are Binding an actual element from the collection that is data bound to the ComboBox.ItemsSource property. As you are not using an actual element from the ItemsSource collection, you might have to do things somewhat differently.
On possible solution is for you to use the SelectedValue, SelectedValuePath and DisplayMemberPath properties instead. Please see the How to: Use SelectedValue, SelectedValuePath, and SelectedItem page on MSDN for full details on using this method to access the selected item.

WPF Editable ComboBox with multi-binding displaying object type when selected

I've got a editable combobox where I'm using an ItemTemplate that uses MultiBinding. The items displayed when the combobox is expanded display as I expect. However, when an item is selected the text displayed is the object type instead. I've seen posts that suggest overriding ToString(), but I'd like to avoid that if possible.
The object I'm bound to is a collection of UserDetail objects, among other UserDetail has a First and Last Name and an BarcodeID that I'm displaying as a string for each item in the ComboBox. I want to display that same string as the selected item. Instead what I'm seeing is MyNameSpace.UserDetail
Here's the xaml for my combobox. Please tell me where I'm going wrong:
<ComboBox IsEditable="True" IsReadOnly="False" Name="myUser"
TextBoxBase.TextChanged="myCombo_TextChanged"
SelectionChanged="myCombo_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{1}, {0}, {2}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
<Binding Path="BarcodeId" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
FYI, The BarcodeID is for an optional barcode badge reader.
You should only bind text data to a combobox that has IsEditable = true.
Remove IsEditable="True"
See here for more info.
Set property
TextSearch.TextPath="your_binding_property_path".
From http://www.shujaat.net/2010/08/wpf-editable-combobox-with-datatemplate.html

Binding as a formatted string

I have a ListBox which hold a set of objects (linked via ItemsSource bind to an ObservableCollection). I haven't used Dynamic binding yet. It currently use the ToString() method of the object. The ToString() method shows a string this way : name (someOtherProperty)
However, even if the INotifyPropertyChanged is implemented and that i use an ObservableCollection, if i change an item property this string won't be updated.
I believe that this is because it only calls ToString once. instead i guess i have to use data binding but how i can form such a string with it ? << name (someOtherProperty) >>
Thanks.
You can use a multibinding, e.g. something like this:
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path="name"/>
<Binding Path="someOtherProperty"/>
</MultiBinding>
If you just let it execute ToString there is no proper binding at all, any notifications will have no effect.
You use it like this:
<ListBox ...>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<!-- The above binding here -->
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

How do I bind combobox text to legacy data not in the drop down list?

The drop-down list (itemssource) of my combobox contains new product request items. I want to bind to legacy data that is not in the drop-down list. To complicate things I'm using multibinding with an IMultiValueConverter to combine fields for display. Also, the names of bound fields do not match the names of the properties I'm bound to.
The combobox itemssource is a list of NewProductRequests. From this NPR object NewProdNumber and NewProdName are combined for display in the drop-down list by my type converter. The ConvertBack method returns the values NewProdNumber and NewProdNumberCombinedWithName. These two values will be saved to database fields with slightly different names. For this example I'll call them DBProdRequestNumber and DBProdRequestTitle.
I've succeeded in displaying and saving new items. The problem is I haven't figured out how to display legacy data that is not in the list. It's not in the list because it no longer qualifies as a new product request.
Here is the problem XAML (the itemssource is set in code-behind):
<ComboBox x:Name="NPRComboBox" IsSynchronizedWithCurrentItem="False" IsEditable="False">
<ComboBox.SelectedItem>
<MultiBinding Converter="{StaticResource combineNPRStuffMultiConverter}">
<Binding Path="DBProdRequestNumber" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="DBProdRequestTitle" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</ComboBox.SelectedItem>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource combineNPRStuffMultiConverter}">
<Binding Path="NewProdNumber" UpdateSourceTrigger="PropertyChanged"/>
<Binding Path="NewProdNumberCombinedWithName" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
A similar problem with a datagrid and combobox I solved using a DataGridTemplateColumn.CellEditingTemplate based on this MSDN Magazine example from Julie Lerman. Of course, in this case I'm not using a datagrid.
Thanks in advance for any help.
This answer (to my own question) was pulled from a comment in the NathanAW answer:
Unfortunately I can't include legacy items in the ItemsSource. The list is from a web service that is out of my control. I devised a kludgy solution which I don't really like (but it works)...Since I know the combobox is needed only for new records it is visible only when the user clicks "Add". In the same location I placed a textbox bound to the legacy data that is visible when NOT in add mode. So, I toggle the visiblity of each control as the app switches in and out of add mode. I'm sure there is a better way!
It seems that you might be able to simplify this by not using a Multi-Binding converter. If you have a collection of NPR objects, then you can set that as the ItemsSource for the listbox. Then use the DataTemplate to format how you want that item displayed.
With this setup, you can construct a template that shows multiple fields from the NPR object in a single TextBlock using something like:
<ComboBox
x:Name="NPRComboBox"
IsSynchronizedWithCurrentItem="False"
IsEditable="False"
SelectedItem={Binding SelectedNPR, Mode=TwoWay}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding Path=NewProdNumber, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
<Run> - </Run>
<Run Text="{Binding Path=NewProdNumberCombinedWithName, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If you have additional properties on the NPR object that you'd like to access, you can add an additional section to the template.
Notice that the "selected" item is bound two-way back to a property on your ViewModel (or code-behind, or whatever). This would be something like:
public NPR SelectedNPR
{
get { ... }
set
{
...
// don't forget INotifyPropertyChanged
...
}
}
EDIT
Here is a sample that seems to do what you've indicted about showing legacy data in the "SelectionBox", but not in the drop down list. To test this, try running it in KaXaml or something. Then start typing "Hello 3" and see that it suggests "Hello 30". This indicates that the Combo knows about the item. Now drop the list down and see that it isn't in the list. If you arrow down with the arrow keys, it skips from "Hello 20" to "Hello 40".
The next step would be to setup your templates so that the ListBoxItem template's Visibility is bound to "IsLegacy" on your NPR object. Then add both legacy and new items to the ItemsSource collection and bind to the list.
<ComboBox IsEditable="True">
<ComboBoxItem >Hello 10</ComboBoxItem>
<ComboBoxItem >Hello 20</ComboBoxItem>
<ComboBoxItem Visibility="Collapsed">Hello 30</ComboBoxItem>
<ComboBoxItem >Hello 40</ComboBoxItem>
</ComboBox>

Resources