I'm new to WPF.
I'm trying to make the SelectedItem for all the comboboxes to generic as there are a lot of ComboBox in my form.
When I use different SelectedItem for each ComboBox it works fine.
C# Code:
private DropDownModel _mySelectedItem_DeviceType;
public DropDownModel MySelectedItem_DeviceType
{
get { return _mySelectedItem_DeviceType; }
set
{
if (_mySelectedItem_DeviceType != value)
{
_mySelectedItem_DeviceType = value;
OnNotifyPropertyChanged("MySelectedItem_DeviceType");
}
}
}
private DropDownModel _mySelectedItem_All;
public DropDownModel MySelectedItem_All
{
get { return _mySelectedItem_All; }
set
{
if (_mySelectedItem_All != value)
{
_mySelectedItem_All = value;
OnNotifyPropertyChanged("MySelectedItem_All");
}
}
}
private DropDownModel _mySelectedItem_Status;
public DropDownModel MySelectedItem_Status
{
get { return _mySelectedItem_Status; }
set
{
if (_mySelectedItem_Status != value)
{
_mySelectedItem_Status = value;
OnNotifyPropertyChanged("MySelectedItem_Status");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnNotifyPropertyChanged(string propertyName)
{
var propertyChangedEvent = PropertyChanged;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
var myListDropDown = (from b in _entities.DeviceTypes
select new DropDownModel
{
ID = b.ID,
Name = b.Name
}).Distinct().ToList();
myListDropDown.Add(new DropDownModel
{
ID = -1,
Name = "Select Device"
});
myListDropDown = myListDropDown.OrderBy(x => x.Name).ToList();
//DisplayDeviceType = new ObservableCollection<DropDownModel>(myListDropDown);
DisplayDeviceType = new ObservableCollection<DropDownModel>(myListDropDown);
MySelectedItem_DeviceType = (from b in myListDropDown
where b.ID == (singleDevice == null ? -1 : singleDevice.DeviceTypeID)
select b).FirstOrDefault();
var allStatus = (from b in _entities.DeviceStatuses
select new DropDownModel
{
ID = b.ID,
Name = b.Status
}).Distinct().ToList();
allStatus.Add(new DropDownModel
{
ID = -1,
Name = "Select Status"
});
allStatus = allStatus.OrderBy(x => x.Name).ToList();
Status = new ObservableCollection<DropDownModel>(allStatus);
MySelectedItem_Status = (from b in allStatus
where b.ID == (singleDevice == null ? -1 : singleDevice.StatusID)
select b).FirstOrDefault();
XAML:
<ComboBox Name="DeviceTypeComboBox"
Grid.Column="1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="143.917"
Grid.Row="1"
ItemsSource="{Binding DisplayDeviceType}"
SelectedItem="{Binding MySelectedItem_DeviceType}"
SelectedValue="ID"
SelectedValuePath="ID"
DisplayMemberPath="Name"
/>
<ComboBox Name="StatusTypeComboBox"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="143.917"
Grid.Column="4"
Grid.Row="1"
ItemsSource="{Binding Status}"
SelectedItem="{Binding MySelectedItem_Status}"
SelectedValue="ID"
SelectedValuePath="ID"
DisplayMemberPath="Name"
/>
// Works Fine
But when I try to replace MySelectedItem_Status & MySelectedItem_DeviceType with MySelectedItem_All in my XAML. It throws an Exception.
Thanks,
It sounds like you need to abstract a bit more and have a class that wraps up all these properties you are trying to set through combo box selected item.
For example
public class DeviceInfo
{
private DeviceDisplayType deviceType;
public DeviceDisplayType DeviceType
{
get { return deviceType; }
set { deviceType = value; }
}
private DeviceStatus deviceStatus
public DeviceStatus DeviceStatus
{
get { return deviceStatus; }
set { deviceStatus = value; }
}
}
And then have one of these DeviceInfo objects in your viewmodel, and make the xaml bind to those properties directly.
<ComboBox SelectedItem={Binding MyDeviceInfo.DeviceType} />
You will still need the properties somewhere, but rather than a disparate range of VM properties you are recognising that those properties are all part of a larger cohesive class.
Related
I've got ComboBox and ListBox of CheckBoxes. Depending on SelectedItem of ComboBox ItemSource of ListBox must change. I made a sample to make thing easier. Here is the code:
ViewModel
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace Test
{
class Data
{
public long Id;
public object Value;
public override string ToString()
{
return Value.ToString();
}
}
class CheckedData: INotifyPropertyChanged
{
private Data myData;
public Data MyData
{
get { return myData; }
set
{
if (myData == value)
return;
myData = value;
RaisePropertyChanged(nameof(MyData));
}
}
private bool isChecked;
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
RaisePropertyChanged(nameof(IsChecked));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class BindingObject: INotifyPropertyChanged
{
private ObservableCollection<Data> dataList = new ObservableCollection<Data>();
public ObservableCollection<Data> DataList
{
get { return dataList; }
set
{
dataList = value;
RaisePropertyChanged(nameof(DataList));
}
}
private Data selectedItem;
public Data SelectedItem
{
get { return selectedItem; }
set
{
if (value == selectedItem)
return;
selectedItem = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
class ViewModel: INotifyPropertyChanged
{
public ViewModel()
{
var tmp = new Data() {Id = 1, Value = "Cat"};
Obj.DataList.Add(tmp);
Obj.SelectedItem = tmp;
Obj.DataList.Add(new Data() {Id = 2, Value = "Dog"});
Mapping[1] = new ObservableCollection<CheckedData>()
{
new CheckedData() {IsChecked = true, MyData = new Data() {Id = 1, Value = "Maine coon"}},
new CheckedData() {IsChecked = true, MyData = new Data() {Id = 2, Value = "Siberian"}}
};
}
private BindingObject obj = new BindingObject();
public BindingObject Obj
{
get { return obj; }
set
{
if (obj == value)
return;
obj = value;
RaisePropertyChanged(nameof(Obj));
}
}
private Dictionary<long, ObservableCollection<CheckedData>> mapping = new Dictionary<long, ObservableCollection<CheckedData>>();
public Dictionary<long, ObservableCollection<CheckedData>> Mapping
{
get { return mapping; }
set
{
if (mapping == value)
return;
mapping = value;
RaisePropertyChanged(nameof(Mapping));
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
View
<ComboBox x:Name="comboBox" ItemsSource="{Binding Path=Obj.DataList}" SelectedItem="{Binding Path=Obj.SelectedItem, Mode=TwoWay}"/>
<ListBox x:Name="listBox" Height="100" ItemsSource="{Binding Path=Mapping[Obj.SelectedItem.Id]}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=MyData.Value}" Margin="0,5,5,0"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
That is what I thought should work. ComboBox is okay, but ListBox ItemSource binding doesn't work. Only if I bind directly to list like this:
ViewModel
private ObservableCollection<CheckedData> test = new ObservableCollection<CheckedData>()
{
new CheckedData() {IsChecked = true, MyData = new Data() {Id = 1, Value = "Maine coon"}},
new CheckedData() {IsChecked = false, MyData = new Data() {Id = 2, Value = "Siberian"}}
};
public ObservableCollection<CheckedData> Test
{
get { return test; }
set
{
test = value;
RaisePropertyChanged(nameof(Test));
}
}
View
<ListBox x:Name="listBox" Height="100" ItemsSource="{Binding Path=Test}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=MyData.Value}" Margin="0,5,5,0"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Everything starts working.. Except Content binding, because I can't go deeper than 1 level of property path. So I have to override ToString() method in Data.
What should I fix to make everything work? Am I able to bind ItemSource like this? Why can't I go deeper than 1 lvl property binding in CheckBox?
Am I able to bind ItemSource like this?
No, this kind of bindings are not supported in XAML:
Binding Path=Mapping[Obj.SelectedItem.Id].
You must replace Obj.SelectedItem.Id with a constant key value like 1 or bind to some other property that returns the collection of items.
Everything starts working.. Except Content binding
You can only bind to public properties so Value must be a property and not a field:
class Data
{
public long Id { get; set; }
public object Value { get; set; }
}
You can achive this easy with:
public ObservableCollection<CheckedData> SelectedData
{
get
{
return Mapping[Obj.SelectedItem.Id];
}
}
And into
public Data SelectedItem
{
get { return selectedItem; }
set
{
if (value == selectedItem)
return;
selectedItem = value;
RaisePropertyChanged(nameof(SelectedData)); // add this.
}
}
Now, in XAML, you can easy:
<ListBox x:Name="listBox" Height="100" ItemsSource="{Binding Path=Obj.SelectedData}">
I have a problem with my binding. Everything works except that the initial value displayed in the combo box of the selected is blank. The drop down has the two values below the blank that is originally display. Any help would be fantastic.
Main Class
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
public Data myData = new Data(new LocationSite("There", 9.81234));
Binding b = new Binding();
b.Source = MainWindow.Data.Location;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("Gravity");
MainWindow.mainWindow.Gravity.SetBinding(TextBox.TextProperty, b);
Binding b = new Binding() { Source = MainWindow.Data.LocationSelection };
MainWindow.mainWindow.LocationComboBox.DisplayMemberPath = "Name";
MainWindow.mainWindow.LocationComboBox.SetBinding(ComboBox.ItemsSourceProperty, b);
//bind selection
MainWindow.mainWindow.LocationComboBox.DataContext = MainWindow.Data;
Binding selectedItemBinding = new Binding() { Source = MainWindow.Data, Path = new PropertyPath("Location"), Mode = BindingMode.TwoWay}
MainWindow.mainWindow.LocationComboBox.SetBinding(ComboBox.SelectedValueProperty, selectedItemBinding);
MainWindow.mainWindow.LocationComboBox.SelectedIndex = 0; // always index 0 but might need index 1 how do I make it use whatever location is?
}
}
Data class with a list of Locations and one location that is the selected. Somehow I need to tell the combo box that the one to select is the location that matched the list. Any Help????
public class Data : INotifyPropertyChanged
{
private LocationSite location;
private List<LocationSite> locationSelection;
public Location(LocationSite useLocation)
{
location = useLocation; // can either be "Here" or "There" need start index either 0 or 1
locationSelection = new List<LocationSite>();
locationSelection.Add(new LocationSite("Here", 9.795884));
locationSelection.Add(new LocationSite("There", 9.81234));
}
public LocationSite Location
{
get { return location; }
set {
if (location == null)
{
location = new LocationSite();
}
Location.Gravity = value.Gravity;
Location.Name = value.Name;
}
}
/// <summary>
/// Getter/Setter of a list of LocationSites
/// </summary>
public List<LocationSite> LocationSelection
{
get { return locationSelection; }
set { locationSelection = value; }
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
The object that I have a list of
public class LocationSite : INotifyPropertyChanged
{
private string name;
private double gravity;
public LocationSite(string siteName, double siteGravity)
{
Name = siteName;
Gravity = siteGravity;
}
public string Name
{
get { return name; }
set { name = value;
this.OnPropertyChanged("Name");
}
}
public double Gravity
{
get { return gravity; }
set { gravity = value;
this.OnPropertyChanged("Gravity");
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
}
}
The XAML file
<Window x:Class="Data.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Needs to be updated" Height="820" Width="1280" HorizontalAlignment="Left">
<Grid Name="MainScreenGrid">
<TextBox x:Name="Gravity" Grid.Column="8" HorizontalAlignment="Left" Height="23" Grid.Row="3" TextWrapping="NoWrap" Text="0.0" VerticalAlignment="Top" Width="140" IsHitTestVisible="False" IsReadOnly="True"/>
<ComboBox x:Name="LocationComboBox" Grid.Column="6" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Top" Width="140" Height="22"/>
</Grid>
</Window>
in your constructor try this
LocationComboBox.SelectedIndex = 0;
In your Data Class Try this
private LocationSite location;
public LocationSite Location
{
get
{
return location;
}
set
{
location=value;
OnPropertyChanged("Location")
}
}
And in MainWindowConstructor Set the Value Like This
MainWindow.Data.Location=MainWindow.Data.LocationSelection.FirstOrDefault();
In this method By default It will Take the First Item of LocationSelection as Location.
And You need to Use System.Linq NameSpace for FirstOrDefault().
Set the Location Value Before You Set the Binding.
I have a particular scenarios. My application looks like this.
In the left side there are some User list Which is a ListBox and at the right side few fields which are data binding to left side. How it works is, if you select "User 1" in the right side user 1 related information will appear and you can modify the information and its is data binding with "UpdateSourceTrigger=PropertyChanged" so it immediately reflects at the left side too. Same case for other users.
Now the problem is if I select multiple users and edit a field say Field 3 which is Editable a textBox. Now If I select user 1 and edit this textbox it reflects in the user 1 "Note: ... " and if I select user 2 and edit the Field 3 it updates the User 2 "Note: ... " but in case of multi selection How do I achieve it? Suppose I want to select user 1 and User 2 both and Edit the Note field It should update both the note fields of user 1 and user 2 and Data binding should also work I mean it should immediately the text i am entering into the textbox. Any ideas how can I achieve this?
Currently in my viewModel
Model
public String Note
{
get
{
return (String)GetValue(NoteProperty);
}
set { SetValue(NoteProperty, value); }
}
View
and in XAML the User ListBox Items template is defined like this
<TextBlock Text="{Binding Note, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
and in the XAML the rightside textbox (field 3) is data bound in the same manner
<TextBox Text="{Binding Note, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
How do I achieve multiple users data binding?
Please help and give me some ideas.
EDIT:
Converter:
public class MultiBindingConverter : IValueConverter
{
ObservableCollection<Info> mycollection;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var coll = (ObservableCollection<Info>)value;
mycollection = coll;
if (coll.Count == 1)
{
if (parameter.ToString() == "FNote")
return coll[0];
}
else if (coll.Count > 1)
{
// string name = coll[0].FirstName;
if (parameter.ToString() == "FNote")
{
string name = coll[0].Note;
foreach (var c in coll)
{
if (c.Note != name)
return null;
else continue;
}
return name;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter.ToString() == "FNote")
{
foreach (var c in mycollection)
{
c.Note = value.ToString();
}
return mycollection;
}
return null;
}
}
For me only one TextBox Editable NoteTextBox needs to to be DataBinded with multiple Users.
In my ViewModel
I have written
ViewModel
private Command selectionChangedCommand;
public Command SelectionChangedCommand
{
get
{
if (selectionChangedCommand == null)
{
selectionChangedCommand = new Command(SelectionChanged, true);
}
return selectionChangedCommand;
}
set { selectionChangedCommand = value; }
}
public void SelectionChanged(object value)
{
selectedItem = new ObservableCollection<Info>((value as IEnumerable).OfType<Info>());
}
private ObservableCollection<Info> selectedItem;
public ObservableCollection<Info> SelectedItem
{
get { return selectedItem; }
set
{
selectedItem = value;
PropertyChanged("SelectedItem");
}
}
In the Info class there is one property Note which needs to be binded to the View's two places.
I fully agree with #GazTheDestroyer ... this kind of Data Binding can not be achieved through Data binding alone. What #Kumar has suggested is working as a POC, but when you are in a live project and you play with model, viewModel and view and many UserControl with one view model or one User control with two ViewModels, then the difficulty of achieving this scenario is beyond guessing.
Ok, no more theory. I have achieved this and I am going to share how I did so.
One-to-one DataBinding is perfect and working fine. When you select User 4 This user Note field and Field3 Editable NoteBox are bound to the same Property, so it works perfectly.
In multiple selection say User4 is selected first, then you select User3 and user1, I put a logic in code behind that when multiple items are selected Note text is empty. This is not against
MVVM as updating a view based on some criteria of view is not breaking MVVM pattern. So now when the editable text box is updated with some text user4 properties is updated in viewModel. Now the difficult part is to update the other selected users. Here is the code that will update the selected users and will reflect as I have mentioned Mode="TwoWay", UpdateSourceTriger="PropertyChanged"
if (listUser.SelectedItems.Count > 1)
{
for (int i = 0; i < listUser.SelectedItems.Count; i++)
{
Info info = listUser.SelectedItems[i] as Info;
info.Note = (string)tbNote.Text;
}
}
In this way the value of the Editable note textbox is updated in the properties of all the users Note Property and as the binding is two-way, it will reflect in other users too.
There might be many way to solve it, but I found this way and it's working superbly, so I thought I'd answer my own question.
You cannot achieve this via databinding alone, since there are situations where you need to make logical decisions.
For instance, if user1 and user2 have different notetext, then when both are selected you cannot show both at the same time. Instead I guess you want some method of specifying that you want to "keep original text", or allow user to over type to set both texts to be the same.
Whatever you intend, you need to have separate binding sources in your viewmodel so that you can update them independently and make logical decisions.
I tried something with i know and i got output just as your requirement.Please correct me if i'm wrong.
XAML
<Window x:Class="MVVM_sample_ListBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVM_sample_ListBox"
Title="MainWindow" Height="350" Width="525"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
<Window.Resources>
<local:Converter x:Key="Converter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="235*" />
<ColumnDefinition Width="268*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="lb" SelectionMode="Multiple" Grid.Row="0" ItemsSource="{Binding MyCollection}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseUp" >
<i:InvokeCommandAction CommandParameter="{Binding SelectedItems, ElementName=lb}" Command="{Binding SelectionChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Text="{Binding SecondName}"/>
<TextBlock Text="{Binding Company}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="1" >
<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Text="{Binding SelectedItem,ConverterParameter=FName, Converter={StaticResource Converter}}" Name="textBox1" VerticalAlignment="Top" Width="120" />
<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Text="{Binding SelectedItem,ConverterParameter=SName, Converter={StaticResource Converter}}" Name="textBox2" VerticalAlignment="Top" Width="120" />
<TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Text="{Binding SelectedItem,ConverterParameter=Comp, Converter={StaticResource Converter}}" Name="textBox3" VerticalAlignment="Top" Width="120" />
</StackPanel>
</Grid>
</Window>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
Model
public class Model : INotifyPropertyChanged
{
private string fname;
public string FirstName
{
get { return fname; }
set { fname = value;RaisePropertyChanged("FirstName"); }
}
private string sname;
public string SecondName
{
get { return sname; }
set { sname = value; RaisePropertyChanged("SecondName");}
}
private string company;
public string Company
{
get { return company; }
set { company = value;RaisePropertyChanged("Company"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string name)
{
if(PropertyChanged!= null)
{
this.PropertyChanged(this,new PropertyChangedEventArgs(name));
}
}
}
ViewModel
public class ViewModel : INotifyPropertyChanged
{
private MyCommand selectionChangedCommand;
public MyCommand SelectionChangedCommand
{
get
{
if (selectionChangedCommand == null)
{
selectionChangedCommand = new MyCommand(SelectionChanged);
}
return selectionChangedCommand;
}
set { selectionChangedCommand = value; }
}
public void SelectionChanged(object value)
{
SelectedItem = new ObservableCollection<Model>((value as IEnumerable).OfType<Model>());
}
private ObservableCollection<Model> selectedItem;
public ObservableCollection<Model> SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; RaisePropertyChanged("SelectedItem"); }
}
private ObservableCollection<Model> mycoll;
public ObservableCollection<Model> MyCollection
{
get { return mycoll;}
set { mycoll = value;}
}
public ViewModel()
{
SelectedItem = new ObservableCollection<Model>();
SelectedItem.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SelectedItem_CollectionChanged);
MyCollection = new ObservableCollection<Model>();
MyCollection.Add(new Model { FirstName = "aaaaa", SecondName = "bbbbb", Company = "ccccccc" });
MyCollection.Add(new Model { FirstName = "ddddd", SecondName = "bbbbb", Company = "eeeeeee" });
MyCollection.Add(new Model { FirstName = "fffff", SecondName = "gggggg", Company = "ccccccc" });
}
void SelectedItem_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//this.SelectedItem =new ObservableCollection<Model>((sender as ObservableCollection<Model>).Distinct());
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string name)
{
if(PropertyChanged!= null)
{
this.PropertyChanged(this,new PropertyChangedEventArgs(name));
}
}
}
public class MyCommand : ICommand
{
private Action<object> _execute;
private Predicate<object> _canexecute;
public MyCommand(Action<object> execute, Predicate<object> canexecute)
{
_execute = execute;
_canexecute = canexecute;
}
public MyCommand(Action<object> execute)
: this(execute, null)
{
_execute = execute;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (parameter == null)
return true;
if (_canexecute != null)
{
return _canexecute(parameter);
}
else
{
return true;
}
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion
}
Converter
public class Converter : IValueConverter
{
ObservableCollection<Model> mycollection;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var coll = (ObservableCollection<Model>)value;
mycollection = coll;
if (coll.Count == 1)
{
if (parameter.ToString() == "FName")
return coll[0].FirstName;
else if (parameter.ToString() == "SName")
return coll[0].SecondName;
else if (parameter.ToString() == "Comp")
return coll[0].Company;
}
else if(coll.Count >1)
{
// string name = coll[0].FirstName;
if (parameter.ToString() == "FName")
{
string name = coll[0].FirstName;
foreach (var c in coll)
{
if (c.FirstName != name)
return null;
else continue;
}
return name;
}
if (parameter.ToString() == "SName")
{
string name = coll[0].SecondName;
foreach (var c in coll)
{
if (c.SecondName != name)
return null;
else continue;
}
return name;
}
if (parameter.ToString() == "Comp")
{
string name = coll[0].Company;
foreach (var c in coll)
{
if (c.Company != name)
return null;
else continue;
}
return name;
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (parameter.ToString() == "FName")
{
foreach (var c in mycollection)
{
c.FirstName = value.ToString();
}
return mycollection;
}
else
if (parameter.ToString() == "SName")
{
foreach (var c in mycollection)
{
c.SecondName = value.ToString();
}
return mycollection;
}
else
if (parameter.ToString() == "Comp")
{
foreach (var c in mycollection)
{
c.Company = value.ToString();
}
return mycollection;
}
return null;
}
}
I am working on a combobox for more than 2 days but did not find the solution.
In one of my question a user named JnJnBoo tried to answer my question and I got some knowledge and code from there.
I am trying to display some data in a combobox in multiple columns using MVVM pattern.
I am using entity framework and SQL Server database.
Here is the code :
namespace ERP_Lite_Trial.ViewModels
{
public class GroupsViewModel : INotifyPropertyChanged
{
public GroupsViewModel()
{
using (DBEntities db = new DBEntities())
{
GroupsAndCorrespondingEffects = (from g in db.Groups
select new GroupAndCorrespondingEffect
{
GroupName = g.Name,
CorrespondingEffect = g.Type_Effect.Name
}
).ToList();
EffectName = (from e in db.Type_Effect
select e.Name).ToList();
}
}
private List<GroupAndCorrespondingEffect> _groupsAndCorrespondingEffects;
public List<GroupAndCorrespondingEffect> GroupsAndCorrespondingEffects
{
get
{
return _groupsAndCorrespondingEffects;
}
set
{
_groupsAndCorrespondingEffects = value;
OnPropertyChanged("GroupsAndCorrespondingEffects");
}
}
private string _selectedGroup;
public string SelectedGroup
{
get
{
return _selectedGroup;
}
set
{
_selectedGroup = value;
OnPropertyChanged("SelectedGroup");
}
}
private List<string> _effectName;
public List<string> EffectName
{
get
{
return _effectName;
}
set
{
_effectName = value;
OnPropertyChanged("EffectName");
}
}
public void OnPropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class GroupAndCorrespondingEffect
{
public string GroupName;
public string CorrespondingEffect;
}
}
And the XAML :
<ComboBox x:Name="cbUnder" ItemsSource="{Binding Path=GroupsAndCorrespondingEffects}"
IsEditable="True" SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
TextSearch.TextPath="GroupName" Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=GroupName}"/>
<TextBlock Text="{Binding Path=CorrespondingEffects}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I tried many things but always unsuccessful.
My Combobox is not displaying any data in its items but when I select any Item in the combobox then I get some data in the selectedGroup property. The data is namespace.classname
So I think I need to override the Tostring Method of GroupAndCorrespondingEffect class. But It has got two properties. In which format the databinding in XAML expects the data that is not known to me. So, how to override the tostring method? Or might be I am making some sort of mistake in my code?
Your GroupAndCorrespondingEffect should look like the following
public class GroupAndCorrespondingEffect
{
public string GroupName;
{
get;
set;
}
public string CorrespondingEffect;
{
get;
set;
}
}
And in you XAML
<TextBlock Text="{Binding Path=CorrespondingEffects}"/>
The property name is wrong it contains additional s
so it should be
<TextBlock Text="{Binding Path=CorrespondingEffect}"/>
public class GroupAndCorrespondingEffect
{
public string GroupName;
public string CorrespondingEffect;
}
Make the public variables GroupName and CorrespondingEffect as properties
And in your View model change the type of the property SelectedGroup like below
private GroupAndCorrespondingEffect _selectedGroup;
public GroupAndCorrespondingEffect SelectedGroup
{
get
{
return _selectedGroup;
}
set
{
_selectedGroup = value;
OnPropertyChanged("SelectedGroup");
}
}
Today I am stucked in very basic concept again. What is the mistake I am doing.
I have XAML like
<ComboBox ItemsSource="{Binding MyItems}" Height="40" Width="200" SelectedIndex="0"
SelectedItem="{Binding MySelectedItem}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding MySelectedItem.Name}"/>
<Button Content="Test" Grid.Row="1" Click="Button_Click_1"/>
My ModelView looks like
public class MainViewModel : DependencyObject,INotifyPropertyChanged
{
private MyModel mySelectedItem;
public MyModel MySelectedItem
{
get
{
return mySelectedItem;
}
set
{
if (value != mySelectedItem)
{
mySelectedItem = value;
RaisePropertyChange("MySelectedItem");
}
}
}
public IList<MyModel> MyItems
{
get
{
return new List<MyModel>() {new MyModel(){Name="A"},
new MyModel(){Name="B"},
new MyModel(){Name="C"}};
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange(string name)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
One MyItems property and one SelectedItem property
and Click handler like
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Random r = new Random();
int icnt = r.Next(0,3);
model.MySelectedItem = model.MyItems[icnt];
}
I found that TextBlock.Text is updating but Combobox selected item is not updating. I try to dig out the reason and found that if I execute code below
MyModel prevItem = model.MyItems.Where((m) => m.Name.Equals("A")).FirstOrDefault();
MyModel newItem = model.MyItems.Where((m) => m.Name.Equals("A")).FirstOrDefault();
bool result = prevItem.Equals(newItem);
The value is always false. But why, why I am getting the new reference to same object from collection.
How can resolve this issue.
Thanks
you are getting a new reference because each time the binding mechanism will ask for MyItems you will create a new list.
try creating it once and use observable collection
You need to modify your MyItems code. You are getting new list every time. Try this out.
private List<MyModel> _myItems;
public IList<MyModel> MyItems
{
get
{
if (_myItems == null)
{
myItems = new List<MyModel>();
myItems.Add(new MyModel() { Name = "A" });
myItems.Add(new MyModel() { Name = "B" });
myItems.Add(new MyModel() { Name = "C" });
}
return _myItems}
}
}