An ItemTemplate Containing A Combobox - How Do I Bind To The SelectedValue? - c#

Imagine your working on a UI for an Wedding Planner app. You'll have a list of guests and you want to display their name on the screen next to a combobox containing values of 'Will Attend' / 'Maybe' / 'No'.
I've tried to something just that....I have a collection of items inside a view model (the guests). For each of those items I want to display a Label and a ComboBox. Each ComboBox has the same values in the drop down (the possible responses).
I've created an ItemTemplate that contains a label and a combobox. I bind it to my collection of guests and it works as expected. I'm using ancestor binding so that the ComboBox's ItemsSource is bound to the list of possible responses. That works great.
What I'm struggling with is how to bind the SelectedItem to get the values the user selects? I want to have a collection of selected values on the ViewModel somehow, but I'm having a lot of trouble finding the correct words to describe this / search for it.
Can anyone help me? Am I going about this the wrong way?

You may create an enum for the attendance state and add an Attendance property to your Guest class:
public enum Attendance
{
Yes,
No,
Maybe
}
public class Guest
{
...
public Attendance Attendance { get; set; } // raise PropertyChanged event if necessary
}
Now you could set the Tag property of the ComboBox items to the appropriate enum value and bind the SelectedValue property:
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding Attendance}" SelectedValuePath="Tag">
<TextBlock Tag="Yes">Will Attend</TextBlock>
<TextBlock Tag="No">Won't Attend</TextBlock>
<TextBlock Tag="Maybe">May Attend</TextBlock>
</ComboBox>
</DataTemplate>
</ItemsControl.ItemTemplate>

Related

WPF Combobox - Displaying Count in Label

I've got a simple WPF ComboBox, displaying Orders/Positions on the Financial Markets.
<ComboBox Name="TradeDropDown"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
ItemsSource="{Binding Path=ActiveOrders}"
DisplayMemberPath="OrderLabel"
SelectedItem="{Binding Path=SelectedOrder, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" />
I need to see at a glance how many items are in the list. I've added a TextBlock above with summary information.
I don't like it, and would prefer to have the items in the dropdown listed like:
(1/2) Working Short 425K
(2/2) Filled Long 979K
etc - and have the 1/2 numbers correctly update as items are added and removed from the list.
The Items are stored in a BindingList.
Is there an easy way to do this?
Is there an easy way to do this?
Add another property to the class where the OrderLabel property is defined that returns a string like "(1/2) Working Short 425K" and set the DisplayMemberPath property of the ComboBox to the name of this property.
Make sure that the class implements the INotifyPropertyChanged interface.
You then set the new property to a new value and raise the PropertyChanged event whenever you want to update the label in the ComboBox.

how can I bind ID property to checkbox in xaml WUP

First thank you guys in StackOverFlow to give the opportunity, u guys doin really great job, I'm very beginner in windows 10 universal apps, I created app which has employee class for example in the models folder let us say like the following:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
and in xaml I created AutoSuggestBox and listView which I bind to ObervableCollection list of type Employee and this as the following:
<ListView ItemsSource="{x:Bind EmployeesList}"
HorizontalAlignment="Left">
<ListView.ItemTemplate>
<DataTemplate x:DataType="data:Employee">
<StackPanel Orientation="Horizontal">
<CheckBox Checked="StatusCB_Checked"
Margin="5,2,5,0"/>
<TextBlock Name="EmpNametxtBlk"
Text="{x:Bind Name}"
VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now once I type name in the AutoSuggestBox the list view will show the checkBox and the employee name , I want to check the checkbox and I want to get the employee object to get deleted from the list view and then add to another list view let's say for chosen employees , my questions is how I can bind the checkBox to the employee ID and then i can use that object in the code behind for the check event ? I mean once i check the checkbox of any added employee on the Listview I wanted to do :
deleted that employee from the ListView and add him in other list view
the other list view will be hidden till I delete employees from the first Listview.
how I can bind the checkbox to the ID of the employee and how I can extract the employee object in the check event or any other even, well I wish I explained what I want in clear way !
how I can extract the employee object in the check event or any other even
You can get the object from DataContext property of the sender object (just make sure you cast the sender from object to FrameworkElement type first)
As #thang2410199 said
You can get the object from DataContext property of the sender object.
You don't need bind the Id property to somewhere for deleting since you can get the employee instance from checkbox's DataContext property. You can delete the checked employee using the following code:
private void StatusCB_Checked(object sender, RoutedEventArgs e)
{
var checkbox = sender as CheckBox;
if(checkbox!=null)
{
var employee =(Employee)checkbox.DataContext;
Employeeslist.Remove(employee);
}
}
A completed demo to meet your requirements you can download here. More details about Listview reference the official sample.
For your list you should use an ObservableCollection.
Implement the INotifyPropertyChanged Interface so the UI is getting notified when data is changing.
Create a new class called ListCountToVisibilityConverter.cs, Implement the IValueConverter interface. When the count of your
Observable collection is > 0 return Visibility.Visible else return
Visibility.Collapsed .
You can delete Items by getting the selected index in the ListView and you can add items by using the add method of the observable collection.
You may want to implement the MVVM pattern. MVVM Wikipedia

Binding lost when using TabControl and MVVM

I still consider myself a beginner when it comes to MVVM and I am having an issue with a binding in a TabControl. My application allows the user to create nations and states, input some information and then save them to a database. Below is a descriptions of how my application is structured:
The base ViewModel/View is called ApplicationViewModel/ApplicationView. The ApplicationViewModel has an ObservableCollection called Tabs consisting of one AllNationsViewModel and one AllStatesViewModel. This Tabs property is bound to the TabControl's ItemsSource in the ApplicationView.
AllNationsViewModel/AllNationsView are used to display all the nations that have been entered by the user. It also allows the user to create new nations and select a particular nation for closer inspection. The AllStatesViewModel/AllStatesView do the same but for states.
Finally I have the NationViewModel/NationsView that deals with a particular nation; also here there are StateViewModel/StateView that do the same for a state.
For a nation you can at the moment only input a name but for a state you can input a name as well as the nation it is part of. The nation is selected using a ComboBox where all the nations created in the nations tab show up.
I use a static class called DataFacade as an interface to my data store. It is possible to add, remove and retrieve a list of nations/states using this interface; also it triggers events when something is added or removed.
The problem I am having is that when there is a state selected in the AllStatesViewModel (CurrentStateViewModel property) and I go to the nations tab and then back to the states tab the currently selected state has lost its nation. All the other states are still ok tough.
I will try to show the relevant code below (I have removed irrelevant code from some methods):
State class:
class State
{
public string Name { get; set; }
public Nation Nation { get; set; }
}
TabControl in the ApplicationView:
<TabControl ItemsSource="{Binding Tabs}" Margin="6">
<TabControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding }" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
When the user creates a nation the AddCommand of the AllNationsViewModel is triggered:
private void Add(NationViewModel vm)
{
DataFacade.AddNation(vm.Nation);
}
The AllStatesViewModel gets notified when a nation gets added to the data store:
private void OnDataStoreNationsChanged(object sender, DataFacadeEventArgs e)
{
Nations.Add(new NationViewModel(e.Context as Nation));
}
The Nations property above is an ObservableCollection of NationViewModels. Now, this property is used by the ComboBox in the StateView to populate its items so a nation can be selected when creating/editing a state:
<ComboBox Grid.Row="1" Grid.Column="1" SelectedValue="{Binding Nation}" SelectedValuePath="Nation" ItemsSource="{Binding DataContext.Nations, RelativeSource={RelativeSource AncestorType={x:Type local:AllStatesView}}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I believe the problem has to do with the binding above. Because if I don't bind to the Nations property in AllStatesViewModel but instead bind to a temporary property in the ApplicationViewModel everything works. Can it be that the AllStatesView is thrown away by the TabControl when I go to another tab therefor the binding above sets the Nation property of my StateViewModel to null? When I debug I see that I get a null when exiting the states tab.
How would someone who is not a beginner like me solve this situation? I find my temporary solution rather ugly. I am not entirely sure how I should handle the data store access since all the MVVM examples I have found don't focus on this part.
EDIT: Addded some pictures as requested:
Atm I just have the simplest testing GUI set-up:
Here you see the AllNationsView, atm the NationView is only the "Name" TextBlock and the TextBox at the top.
Here is the AllStatesView, at the top is the currently selected state (displayed using StateView). Where you now see that USA is selected as nation for Montana, if I go to the Nations tab and then back to the States tab the nation for Montana is now blank. If I select Florida it still has USA as its nation.
WPF only keeps the UI for the active tab in memory. When you change tabs that UI is destroyed and the UI of the new tab is created and rebound.
There are a few ways around the problem you are having. You can use the Repository Pattern to store and access your data sources separate from the view models. Basically, an outside object holds your data sources, such as the lists of states and nations. That way they don't get destroyed when the active tab changes.
The other option is to store the data sources on your ApplicationViewModel and access them via a references to the ApplicationViewModel on each individual tab's view model. You shouldn't have to use a RelativeSource binding anywhere in that.

WPF ComboBox How to bind the selected item in code?

I have a combo box which is populated on selection's from two other combo'.
No problem I have this working.
Problem is I only want to activate the Selected Item binding after this has happened.
Here is my combo
<cuc:ComboBox Name="GopLenTypeCombo" Width="240" Height="24"
IsSynchronizedWithCurrentItem="True"
SelectedValue="{Binding Mode=TwoWay, Source={StaticResource ProfileDataProvider}, XPath=GopLenType}"
IsEnabled="False"/>
How do I stop the SelectedValue binding working untill I have populated the combo ?
I don't know whether this is possible in pure XAML, but if you have an underlying ViewModel, this wouldn't be too hard:
What you would need to do is to bind your third ComboBox to a property on the ViewModel (let's call this property Combo3Items).
You would also need to bind the two first ComboBoxes to properties on the same ViewModel (let's call these Combo1Items and Combo2Items, respectively). In addition to that, you could bind both of these ComboBoxes' SelectedValue to properties on the ViewModel, so that the ViewModel knows which items are selected.
Initially, Combo3Items would be empty, but as soon as the two bound SelectedValues are properly assigned, you can fill Combo3Items and raise an INotifyPropertyChanged event.
The MVVM architecture is very powerful. If you don't already know it, you can read more about it here.

Editable WPF ListBox

I have a ObservableCollection that's bound to a ListBox in WPF. I want the ListBox to be editable, and for the editing changes to be saved to the collection. Since WPF doesnt provide an editable listbox, I've tried creating my own by changing the ListBox.ItemTemplate.
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="EditableText" Text="{TemplateBinding Content}"/>
</DataTemplate>
</ListBox.ItemTemplate>
Changing the ItemTemplate gives me editable boxes, but any changes to the textboxes dont get saved to the ObservableCollection. Is there a way to have an editable ListBox with two way binding?
You cannot do it this way.
To achieve that kind of trick, you would need your items to be "holder classes" that expose a property you can bind your textbox to.
To understand it, imagine the following pseudo sequence of calls:
class ListBox
{
Bind(Items)
{
foreach(var item in Items)
{
DataTemplate Template = LoadTemplateForItem(item.GetType()); // this is where your template get loaded
Template.Bind(item); //this is where your template gets bound
}
}
}
Your template (the DataTemplate with the listbox) is loaded and the item (which I assume is a string in your case) gets passed in.
At this point, it only knows the string, and cannot influence anything upwards. A two-way binding cannot influence the collection because the template does not know in which context it is being used, so it cannot reach back to the original collection and modify its contents.
For that matter, this is the same thing for the TextBox. If it is not given a conainer and a property name, it has nowhere to "store back" the changes.
This basically the same as passing a string into a function call. The function cannot change which string was passed in (ignoring tricks such as by-reference argument passing).
To get back to your case, you need to build a collection of objects which expose a property containing the value that needs to be edited:
public class MyDataItem
{
string Data { get; set;}
}
Then you can bind your ListBox to a collection of those items and modifiy your datatemplate:
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Name="EditableText" Text="{Binding Data, Mode=TwoWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
Bind to a model property -- i.e. a property of the data object -- rather than to a view property such as Content. For example:
// model class
public class Widget : INotifyPropertyChanged
{
public string Description { ... }
}
<!-- view -->
<DataTemplate>
<TextBox Text="{Binding Description}" />
</DataTemplate>
Note this will not work if your ItemsSource is ObservableCollection (because there's no property to bind to).

Categories