How to get checked checkbox content in windows phone app? - c#

I am developing windows phone app .In app,I want to put multiple check box.I able to put multiple check box.But when i checked on check box i want getting its content(check box content).For that i am use checked event and also click event but i cant get result as i want.My xaml code is as below:
<ListBox Name="hobbylist" ItemsSource="{Binding}" Margin="0,0,10,10" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="hobbycheck" Content="{Binding Path=Title}"
IsChecked="{Binding IsSelected}" ClickMode="Release"
Click="CheckBox_Click" ></CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Please help me ...

I think you are using the Checkbox not correctly according to its purpose.
Checkbox should represent a state (e.g. yes/no) regarding a subject. Still, you just have to use the Checked event when the checkbox gets checked and Unchecked otherwise.
So in the Checked event, get the content you wish.
Edit
You have to maintain this with the MVVM pattern somehow. For that, there are plenty of examples in the internet, I am sure you can handle that.
Instead of having Click="CheckBox_Click", use the Check event :
private void CheckBox_Checked (Object sender, EventArgs e)
{
var currentCheckBoxItem = sender as CheckBox;
if (currentCheckBoxItem.IsChecked == true)
{
//you manipulation here...
}
}
Still. this might just not work, because you haven't provided enough details of your matter.
Edit 2 A little of MVVM...
First, make a Hobby model class, with a single string property (you might change your mind later to add more properties, Idk) :
public class Hobby : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
private bool _isSelected;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged();
}
}
//You can add some multiple properties here (***)
public Hobby (string hobbyName, bool isSelected)
{
Name = hobbyName;
IsSelected = isSelected;
}
//INotifiyPropertyChanged interface member implementation ...
}
(* ) For example, a short description and then bind it on the View. The major advantage of this MVVM pattern is logic separation, so if something has to change, the separation of each component makes it easier.
Second, create a ViewModel class (you should implement INotifyPropertyChanged interface) :
public class HobbiesViewModel : INotifyPropertyChanged
{
private ObservableCollection<Hobby> _hobbies;
public ObservableCollection<Hobby> HobbiesCollection
{
get
{
return _hobbies;
}
set
{
_hobbies = value;
OnPropertyChanged();
}
}
//Constructor
public HobbiesViewModel
{
HobbiesCollection = new ObservableCollection<Hobby>();
}
//INotifyPropertyChanged interface member implementation ...
}
Third, create an instance of the ViewModel (the ObservableCollection). Use this quick help out : In the App.xaml.cs, create a static object and use it through the app as you need it :
public partial class App
{
//This already exists in your app's code, but I've written it to
//make an idea where to write the Hobbies object
public static PhoneApplicationFrame RootFrame { get; private set; }
public static HobbiesViewModel Hobbies;
//Again, the already existing constructor
public App()
{
...
Hobbies = new HobbiesViewModel();
}
Now, you almost have it all set; You have the Model, you have the ViewModel, all that's left is to create the connection with the View. This can be easily done through binding. The ViewModel represents the DataContext of your control (in your case the LongListSelector, so in that View's (Page's) constructor, write the following statement :
yourListControlName.DataContext = App.Hobbies;
Now the binding is the only thing left. This is done in XAML code. I won't put a whole chunk of XAML code here, cause you know best how your control looks like. Still, judging by the short sample you provided, there a few adjustments only :
The items source of the list XAML control will be bound to the ObservableCollection object name of the ViewModel representing the control's DataContext. A bit fuzzy, huh? To be clearer, in this case, you need to write ItemsSource="{Binding HobbiesCollection}", the ObservableCollection. Also, in the template, you should have that CheckBox which is bound on your Model's properties :
<DataTemplate>
<StackPanel Orientation="Horizontal"> //StackPanel is kinda useless if you have
//only one child control in it. But I wrote
//according to your code.
<Checkbox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
Now, here things are a bit unclear to me. Why would you use the Checkbox? I've thought of the next possible scenario : You come with some string of your hobbies through deserialization of the Json Data. To add them to the ViewModel, you need only :
App.Hobbies.HobbiesCollection.Add(new Hobby("firstHobbyFromJson", true));
App.Hobbies.HobbiesCollection.Add(new Hobby("secondHobbyFromJson", true));
This would make all hobbies already selected in the View. I guess, you would add some other hobbies, the user doesn't have which are not selected and could add them now :
App.Hobbies.HobbiesCollection.Add(new Hobby("aNewHobby", false));
App.Hobbies.HobbiesCollection.Add(new Hobby("anotherNewHobby", false));
At this point, the user has all its previous hobbies in the list and as well some new hobbies you provided him. After his selection is done, if you need to serialize the Json with only the selected hobbies, you could get like this :
var userHobbies = App.Hobbies.HobbiesCollection.Where(h => h.IsSelected);
or with a foreach and get only those hobby objects which have the IsSelected property as true.
Good luck!

I found a Simpler solution.
My Model
You need to use two variables otherwise you may get an 'stackoverflowexception'
public class ModelObj
{
public int position { set; get; }
public bool isChecked
{
get { return IsChecked; }
set { IsChecked = value; }
}
public bool IsChecked;
}
Code to be added in xaml:
isChecked in xaml sets the ListView Checkbox
Mode=TwoWay updates the isChecked boolean value of the model class
<CheckBox IsChecked="{Binding isChecked , Mode=TwoWay}" Checked="checkBox_Checked" >
c# Code that handles the event
private void checkBox_Checked(object sender, RoutedEventArgs e)
{
foreach (ModelObj obj in listItem)
{
if (obj.isChecked == true)
{
int selectedPosition = obj.position;
}
}
}

Related

Advanced MVVM scenario in UWP app - How to use a model class for multiple viewmodels?

I have been coding for over 4 years now... and time has come that I need to understand how to use MVVM in a perfect way, in order to be able to accomplish more difficult coding tasks.
More specifically, I would like to know how to use a model object for multiple viewmodels and views.
Sample scenario
Let's have an app that has two views: the first with a list of ItemModel objects (that shows all the objects available) and another one that has a list that only contains the favorites ones.
The ItemModel has an IsFavorite boolean property to determine whether or not it is one.
Until today, I have always accomplished scenarios like this in this way:
ItemModel class (a model-viewmodel class at the same time):
public class ItemModel : BaseBind // This class implements INotifyPropertyChanged
{
public ItemModel()
{
// ...
}
private bool isFavorite;
public Boolean IsFavorite
{
get { return isFavorite; }
set { SetProperty(ref isFavorite, value); OnPropertyChanged(); }
}
// All the model's and viewmodel's properties and functions are stored here...
public void GenericFunc()
{
App.Current.AppViewModel.GenericAppFunc(this);
}
public void FavoriteFunc()
{
App.Current.AppViewModel.FavoriteAppFunc(this);
}
public ItemModel Clone()
{
// Cloning the item with "new" constructor...
}
}
AppViewModel class:
public class AppViewModel
{
public AppViewModel()
{
// Initializing...
}
public ObservableCollection<ItemModel> ItemsList { get; set; }
public ObservableCollection<ItemModel> FavoritesList { get; set; }
public void UpdateFavorites()
{
FavoritesList.Clear();
foreach (var it in ItemsList)
{
if (it.IsFavorite) FavoritesList.Add(it.Clone());
}
}
public void GenericAppFunc(ItemModel item)
{
// ...
}
public void FavoriteAppFunc(ItemModel item)
{
// ...
}
}
First view, with all items listed:
<ListView ItemsSource="{x:Bind ViewModel.ItemsList, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:ItemModel">
<StackPanel>
<TextBlock Text="{x:Bind ItemTitle, Mode=OneWay}"/>
<Button Content="Generic func" Click="{x:Bind GenericFunc}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Second view, with only favorites listed:
<ListView ItemsSource="{x:Bind ViewModel.FavoritesList, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="vm:ItemModel">
<StackPanel>
<TextBlock Text="{x:Bind ItemTitle, Mode=OneWay}"/>
<Button Content="Favorite func" Click="{x:Bind FavoriteFunc}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is my way of doing it all... having two completely independent lists and cloning objects everytime the user navigates to the favorites view.
It works, it always did it... BUT this is definitively NOT a good pattern.
To summarize, this is what I'm doing till now...
...and this is what I would like to achieve:
In order to have lightweight models like this...
public class ItemModel
{
public ItemModel()
{
// ...
}
public Boolean IsFavorite { get; set; }
// Only the model's properties and functions are stored here...
}
...and use it for multiple viewmodels/views.
Best regards and thank you for your attention.
In UWP there is no filtering of data on top of a single data source. While in WPF it was possible to use the same ObservableCollection of items and filter only when binding your views using a CollectionViewSource, it seems like the only solution is having two different collections like you are doing.
There is indeed no Filter property in the CollectionViewSource class for UWP so [...] you will have to filter the source collection itself (or use two different collections like you are already doing).
Surely you can remove the need to call UpdateFavorites() by hooking to the PropertyChanged event of your items and move them from one collection to the other when their IsFavorite property is set, like this:
public AppViewModel()
{
// Subscribing to changes in collection of items
ItemsList .CollectionChanged += ItemList_CollectionChanged;
}
private void ItemList_CollectionChanged(object sender, EventArgs e) {
//Here subscribe/unsubscribe to PropertyChanged event of each item
//as they come and go in the collection
}
private void Item_PropertyChanged(object sender, EventArgs e) {
//Here check IsFavorite property and add/remove the item from the favorites list.
//You can even instantiate another type of viewmodel object specific to favorites items.
}
Regarding the MVVM pattern, I would advice to have a lightweight ItemModel model like you suggest and have a collection of them stored in your model. Then you could have two collections, one with ItemViewMode objects and one with FavoriteItemViewModel objects in your AppViewModel. You could also have a single ItemViewModel class with an IsFavorite property and use triggers and styles to have them display differently.
I don't see a reason to clone.
What would be the problem with:
foreach (var it in ItemsList)
{
//if (it.IsFavorite) FavoritesList.Add(it.Clone());
if (it.IsFavorite) FavoritesList.Add(it);
}
or, just set up the FavoritesList as a filtered version:
public List<Item> FavoritesList => ItemsList.Where(x => x.IsFavorit);
as long as you Notify at the right actions this should work.

Adding/removing rows of controls

I have a panel and my idea is to have it populated by a stack panel containing two text boxes. When the user enters something in the left box, something should be generated in the right one, as follows.
<StackPanel Orientation="Horizontal">
<TextBox Name="Lefty" LostFocus="FillMyBuddy" />
<TextBox Name="Righty" LostFocus="FillMyBuddy" />
</StackPanel>
However, I'd like to add an option to add/remove rows and, since I wish not to limit myself to the number of such, I get a bit uncertain regarding the approach on two points.
Manipulating DOM (well, it's XAML/WPF but you see what I'm aiming at).
Event handling.
Is it a big no-no to programmatically affect the mark-up structure of the window? Or is it OK to add/remove panels during run-time?
What would the recommended way to be if I want the Lefty number 3 change stuff in Righty number 3? Anything more neat than checking the sender and pulling its siblings from the parent? I want to use a single event handler for any and all rows (knowing that the operations are always intra-row-wise).
You will want to follow MVVM, and have no code in your code-behind (programmatically affect the mark-up structure) files. The concept is easy when you grasp it, so learn it before you start writing your code.
In short, you are going to want to have a view model (something that implements INotifyPropertyChanged (INPC)) which holds your collection of items (which are going to be models, or view models in pure-MVVM). In "hybrid"-MVVM you could just have your models implement INPC.
Then, through the use of commands, you'd implement the logic to remove items from the list that its in. You can pass references, raise notification, using event bubbling, etc. (it's your preference) to have the item actually removed. In my case, I just passed a "manager" to the hybrid-model and held a reference to that. When the command is called (button is clicked), the model calls for the reference to remove itself from the list.
After you do that you define a DataTemplate to define what an "item" should look like one the View. You use a ItemsControl to show a collection of items, and bind to its ItemsSource so the collection of items are shown. Set your ItemsControl.ItemTemplate to the DataTemplate you created, and anything added to the collection bound to ItemsSource of the type defined in DataTemplate.DataType will render as you specify in the DataTemplate.
At the end of the day, you should learn about MVVM design, DataContext, INPC, Commands, Control types and their "main" properties, e.g. everything that inherits from ItemsControl has an ItemsSource property.
Here is a working example, where changing the original string, will reverse it and put it in the read-only right side text box:
MainWindow.xaml.cs (code-behind)
public partial class MainWindow : Window
{
StructureVm _struct = new StructureVm("Test");
public MainWindow()
{
InitializeComponent();
DataContext = _struct;
}
}
MainWindow.xaml (View)
<Window x:Class="DataTemplateWithCommands.MainWindow"
xmlns:local="clr-namespace:DataTemplateWithCommands"
Title="MainWindow" Height="350" Width="525" Background="Orange">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Model}"
x:Key="VmItem">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Original, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Text="{Binding Encoded}"
IsReadOnly="True" />
<Button Content="X"
Command="{Binding RemoveMeCommand}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource VmItem}">
</ItemsControl>
</Grid>
</Window>
Interface (helpful for Dependency Injection)
public interface IStructureManager
{
bool RemoveItem(Model itemToRemove);
}
ViewModel
public class StructureVm : IStructureManager
{
private readonly ObservableCollection<Model> _items;
private readonly string _title;
public StructureVm(string title)
{
_title = title;
_items = new ObservableCollection<Model>
{
new Model(this, "12"),
new Model(this, "23"),
new Model(this, "34"),
new Model(this, "45"),
new Model(this, "56"),
new Model(this, "67"),
new Model(this, "78"),
new Model(this, "89"),
};
}}
public ObservableCollection<Model> Items
{
get
{
return _items;
}
}
public string Title
{
get
{
return _title;
}
}
public bool RemoveItem(Model itemToRemove)
{
return _items.Remove(itemToRemove);
}
}
Model (not pure-MVVM, pure MVVM models don't implement INPC, and don't have Command in them)
public class Model : INotifyPropertyChanged
{
private readonly RelayCommand _removeMe;
private string _original;
private string _encoded;
private readonly IStructureManager _manager;
public string Original
{
get
{
return _original;
}
set
{
_original = value;
Encoded = ReverseString(_original);
NotifyPropertyChanged();
}
}
public string Encoded
{
get
{
return _encoded;
}
set
{
_encoded = value;
NotifyPropertyChanged();
}
}
public ICommand RemoveMeCommand
{
get
{
return _removeMe;
}
}
public Model(IStructureManager manager, string original)
{
Original = original;
_manager = manager;
_removeMe = new RelayCommand(param => RemoveMe(), param => CanRemoveMe);
}
private void RemoveMe()
{
_manager.RemoveItem(this);
}
private bool CanRemoveMe
{
get
{
//Logic to enable/disable button
return true;
}
}
private string ReverseString(string s)
{
char[] arr = s.ToCharArray();
Array.Reverse(arr);
return new string(arr);
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
RelayCommand implementation
From here on out all you have to do is change the attributes of your controls to whatever you're happy with and call it good. The example might be ugly, but I'm leaving it as an exercise for you to figure out other properties/attributes of WPF controls.

How to make a list of buttons dynamically and show them in the MainForm in a ListBox or ItemsControl

I am new in the WPF.
I want to make the list of buttons from a list of a class say "Buttons" and having two fields (ButtonContent,ButtonID) in the MainForm in WPF.
The idea behind this is that when the MainForm will be loaded then i want to make buttons list dynamically.
The Best Example is already given in the Link
http://www.codeproject.com/Articles/25030/Animating-Interactive-D-Elements-in-a-D-Panel
But i only want to make the equal size buttons stacked horizontally.
How can i do this in WPF? Thank you in advance.
This is the way I would do it. There are some areas here that will need further research on your part, but this will get you started.
First you need your ViewModel. This is the plain old object. It exposes the properties and methods that are pertinent to your business. You mentioned ButtonContent and ButtonID. Let's assume that both of those are strings for now. You'll also need a command for your button I assume.
public class ButtonViewModel : INotifyPropertyChanged
{
private string _content;
public string Content
{
get{ return _content; }
set{ _content = value; OnPropertyChanged("Content"); }
}
// you'll need to implement INotifyPropertyChanged
// also take a look at RelayCommand
// here is your command for you're button
public ICommand ButtonCommand
{
get { return new RelayCommand(execute, canExecute); }
}
private void execute()
{
// my actual command code
}
private bool canExecute()
{
// this will automatically toggle the enabled state on my button
}
}
You'll have one more view model. This will be the DataContext of your MainWindow.
public class AppViewModel
{
public ObservableCollection<ButtonViewModel> MyButtons {get; set; }
public AppViewModel()
{
MyButtons = new ObservableCollection<ButtonViewModel>();
// add some demo data
MyButtons.Add(new ButtonViewModel() {ButtonContent = "Click Me!"});
}
}
Now you need to implement the view. In the MainWindow xaml code. Add your xmlns:local namespace.
<Window.DataContext>
<local:AppViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding MyButtons}">
<ListBox.ItemsTemplate>
<DataTemplate>
<Button Command="{Binding ButtonCommand}" Content="{Binding Content}"/>
</DataTemplate>
<ListBox.ItemsTemplate>
</ListBox>
Hopefully this can get you started in the right direction.

set SelectedIndex of Combobox in WPF to 0

I am binding a Collection at run time to a Combobox and I would like to set the Index after to 0. I could not find a straight answer to what I want.
_stationNames = new ObservableCollection<string>(_floorUnits.Unit.Select(f => f.Name));
_stationNames.Insert(0, "All");
stationsComboBox.ItemsSource = _stationNames;
stationsComboBox.SelectedIndex = 0;//Doesn;t work
Xaml
<ComboBox x:Name="stationsComboBox" Grid.Row="1" Grid.Column="1" Text="{Binding Name}"
SelectionChanged="StationComboBoxSelectionChanged" VerticalAlignment="Center" Margin="3"
SelectedIndex="0"/>
It sounds like you're trying to use it like you would with WinForms. WPF is a slightly different beast and a lot more powerful regarding bindings.
I recommend reading a bit on MVVM to get the most benefit from WPF. By binding the XAML to a view model class (rather than trying to wire things up in Code-behind) you will find you can accomplish what you want with a lot more flexibility without oodles of code.
For instance: Given the following VM:
public class MyViewModel: INotifyPropertyChanged
{
public ObservableCollection<string> StationNames
{
get;
private set;
}
public Something()
{
StationNames = new ObservableCollection<string>( new [] {_floorUnits.Unit.Select(f=>f.Name)});
StationNames.Insert(0, "All");
}
private string _selectedStationName = null;
public string SelectedStationName
{
get
{
return _selectedStationName;
}
set
{
_selectedStationName = value;
FirePropertyChanged("SelectedStationName");
}
}
private void FirePropertyChanged(string propertyName)
{
if ( PropertyChanged != null )
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You can set your view's (XAML form) DataContext to an instance of the ViewModel and update your combo box definition to:
<ComboBox x:Name="stationsComboBox" Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding Path=StationNames}" SelectedItem={Binding Path=SelectedStationName} VerticalAlignment="Center" Margin="3"
SelectedIndex="0"/>
From here whenever the combo box selection changes, the VM's SelectedStationName updates to reflect the current selection, and from anywhere in the VM code, setting the VM's SelectedStationName will update the combo's selection. (I.e. implementing a Reset button, etc.)
Normally though, with something like what you've suggested, I would be looking at binding directly to the Units collection. (or VM's derived from units if they themselves can be viewed/edited.) In any case it should give you a bit of a starting point to start researching into WPF bindings.

How to group checkboxes in treeview wpf mvvm when selection range is [0,1]

I have made a tree View in wpf Using MVVM .
it is working fine but here is one problem that leaf node contains some checkboxes and user have only two options either to select one or none .
So here how i can restricted user to select maximum only one cold drink.
I did one trick but it didn't work that when i have already selected a drink and then i select another one than i set the last selected value in the observable collection to false but it doesn't affect on view and selected check boxes remains selected although in collection only one option's value is true.
I cant use radio button instedof checkbox becasue user can select none of the options and i cant give an additional option for none of the above.
If any one have any solution so please let me know I'll be very thankful.
updated question:
i think i didn't define my problem in a proper way so i am giving my code snipperts here hope by this i'll get the solution o f my problem...
My View Model Class
namespace TestViewModels
{
public class ViewModel :ViewModelBase
{
private ObservableCollection<AvailableProducts> _MyTreeViewProperty
public ObservableCollection<AvailableProducts> MyTreeViewProperty
{
get { return _MyTreeViewProperty
set { _MyTreeViewProperty value;
RaisePropertyChanged("MyTreeViewProperty");}
}
}
public class AvailableProducts
{
private string _BrandName;
public string BrandName
{
get { return _BrandName
set { _BrandName = value; }
}
private bool _IsExpanded;
public bool IsExpanded
{
get
{
return _IsExpanded;
}
set
{
_IsExpanded = value;
}
}
private ObservableCollection<ProductTypes> _MyProductTypes
public ObservableCollection<ProductTypes> MyProductTypes
{
get { return _MyProductTypes}
set { _MyProductTypes= value; }
}
}
public class ProductTypes
{
private string _ProductTypeName;
public string ProductTypeName
{
get { return _ProductTypeName;
set { _ProductTypeNamevalue; }
}
private ObservableCollection<ProductSubTypes> _ProdSubTypes;
public ObservableCollection<ProductSubTypes> ProdSubTypes
{
get { return _ProdSubTypes;}
set { _ProdSubTypes;= value; }
}
}
public class ProductSubTypes
{
private string _ProductSubTypeName;
public string ProductSubTypeName
{
get { return _ProductSubTypeName;
set { _ProductSubTypeName;}
}
private int _ParentID;
public int ParentID
{
get { return _ParentID;}
set { _ParentID;= value; }
}
private bool _IsAssigned;
public bool IsAssigned
{
get { return _IsAssigned; }
set
{
_IsAssigned = value;
if _ParentID;!= 0)
{
//updating data in database
//Calling and setting new collection value in property
//issue : updated collection sets in setter of MyTreeViewProperty but before calling getter
// it comes to IsAssigned getter so view doesnt get updated collection of MyTreeViewProperty
}
RaisePropertyChanged("IsAssigned");
}
}
}
}
View
<Page x:Class="ShiftManagerViews.Pages.ProductTreeSelection
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding ProductsTree, Source={StaticResource Locator}}"
mc:Ignorable="d" Width="870" Height="665"
>
<TreeView Margin="10,10,0,13" ItemsSource="{Binding MyTreeViewProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left"
VerticalAlignment="Top" Width="800" Height="Auto" MinHeight="400" MaxHeight="800">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:AvailableProducts}"
ItemsSource="{Binding MyProductTypes}">
<WrapPanel>
<Image Width="20" Height="20" Source="/ShiftManagerViews;component/Images/12.bmp"/>
<Label Content="{Binding BrandName}" FontSize="14"/>
</WrapPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:ProductTypes}"
ItemsSource="{Binding ProdSubTypes}">
<WrapPanel>
<Image Width="18" Height="15" Source="/ShiftManagerViews;component/Images/12.bmp"/>
<Label Content="{Binding ProductTypeName}" FontSize="13"/>
</WrapPanel>
</HierarchicalDataTemplate>
<!-- the template for showing the Leaf node's properties-->
<DataTemplate DataType="{x:Type local:ProductSubTypes}">
<StackPanel>
<CheckBox IsChecked="{Binding IsAssigned, Mode=TwoWay}" Content="{Binding ProductSubTypeName}" Height="25">
</CheckBox>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
What about using a ListBox to display sub-items instead of a TreeView? You can style that so the items contain a CheckBox to show IsSelected instead of highlighting the item.
I'd suggest your user interface is wrong. If the user can only pick one then it would be better to swap these for radio buttons and add a "None of the above" option. That'll then give you the behaviour you want for free and your UI will be more intuitive.
EDIT: Since you say you can't add a "None" option and want to use a checkbox (even though I strongly disagree on checkboxes where a radio button is more appropriate - a common UI error)...
The technical problem you are probably facing is that an ObservableCollection only raises notification events if the collection itself changes. i.e. Only if items are added or removed. It does not raised events when items within the collection change, therefore the changing the status of the checkbox in the code will not raise the event for the UI binding to act on.
One solution to this to write a custom class that extends ObservableCollection that does provide this behaviour
From MSDN:
If you need to know if someone has changed a property of one of the
items within the collection, you'll need to ensure that the items in
the collection implement the INotifyPropertyChanged interface, and
you'll need to manually attach property changed event handlers for
those objects. No matter how you change properties of objects within
the collection, the collection's PropertyChanged event will not fire.
As a matter of fact, the ObservableCollection's PropertyChanged event
handler is protected—you can't even react to it unless you inherit
from the class and expose it yourself. You could, of course, handle
the PropertyChanged event for each item within the collection from
your inherited collection
I upvoted Rachel's answer, it is a common way in WPF to databind sets of radio buttons or check boxes. If you still want to go the tree view way, below code works. All view related code is in the view, so below code follows MVVM principles. If you are a MVVM purist you can put the code behind and a TreeView control in a user control if you do not want any code behind.
XAML:
<TreeView ItemsSource="{Binding Path=Drinks}">
<TreeView.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding .}" Checked="OnCheckBoxChecked" Unchecked="OnCheckBoxUnchecked" Loaded="OnCheckBoxLoaded" />
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code behind + VM:
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = new VM();
}
private void OnCheckBoxChecked(object sender, System.Windows.RoutedEventArgs e)
{
foreach (CheckBox checkBox in _checkBoxes.Where(cb => cb != sender))
{
checkBox.IsChecked = false;
}
(DataContext as VM).CurrentDrink = (sender as CheckBox).Content.ToString();
}
private void OnCheckBoxUnchecked(object sender, System.Windows.RoutedEventArgs e)
{
(DataContext as VM).CurrentDrink = null;
}
private void OnCheckBoxLoaded(object sender, System.Windows.RoutedEventArgs e)
{
_checkBoxes.Add(sender as CheckBox);
}
private List<CheckBox> _checkBoxes = new List<CheckBox>();
}
public class VM
{
public List<string> Drinks
{
get
{
return new List<string>() { "Coffee", "Tea", "Juice" };
}
}
public string CurrentDrink { get; set; }
}
I did one trick but it didn't work that when i have already selected a
drink and then i select another one than i set the last selected value
in the observable collection to false but it doesn't affect on view
and selected check boxes remains selected although in collection only
one option's value is true.
Make sure that your child objects (AvailableProducts
and SubProductTypes) also implement INotifyPropertyChanged, this will make sure that the UI receives changes when modify the object.
Once all of you objects update the UI properly you will be able to layer in, and test, whatever custom business logic you need.
So if you have a product type that can only have one sub chosen, you could add a property on ProductType called OnlyAllowOneChild. Whenever, a child object raises a IsAssigned changed event, the parent can set false all other children. This of course requires you to have the parent either register for the children's PropertyChangedEvent, or got grab an EventAggregator (MVVMLight Messenger, or PRISM EvenAggregator) and create a messaging system.
Finally i am succeeded to solve my problem.
on Is Assigned property i am updating my database values and calling a method in view using MVVM Light messaging and passing currently selected leaf's parent id in it as a parameter...
Added a property in class Product Types to expand the parent node of the last selected leaf..
In view's method i am refreshing data context's source and passing currently selected leaf's parent id tO the VM to set its Is Expanded property value to true...
By this my view is working perfectly as same as i want...
If any body have solution better than this than I'll be happy to know.

Categories