Combobox selectedValue binding a class 's property not update - c#

public class Rma
{
public int Id { get; set; }
}
and this property:
public static readonly DependencyProperty ActRmaProperty =
DependencyProperty.Register("ActRma", typeof(Rma), typeof(MainWindow),new PropertyMetadata(null));
public Rma ActRma
{
get { return (Rma)GetValue(ActRmaProperty); }
set { SetValue(ActRmaProperty, value); OnPropertyChanged("ActRma"); }
}
public static readonly DependencyProperty ItemsByIdProperty =
DependencyProperty.Register("ItemsById", typeof(ObservableCollection<int>), typeof(MainWindow), new PropertyMetadata(null));
public ObservableCollection<int> ItemsById
{
get { return (ObservableCollection<int>)GetValue(ItemsByIdProperty); }
set { SetValue(ItemsByIdProperty, value); OnPropertyChanged("ItemsById"); }
}
and the combobox in xaml:
<ComboBox ItemsSource="{Binding ItemsById}" DisplayMemberPath="Id"
SelectedValuePath="Id" SelectedValue="{Binding ActRma.Id , UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectionChanged="ComboBox_SelectionChanged"
Margin="3"
AlternationCount="1"/>
thewhen I change via code behind the value of Id the copmbobox not show the value .
e.g:
ItemsById = new ItemById();
ItemsById.Add(1);
ItemsById.Add(2);
ActRma.Id = 2;

To expand on XAMlMAX's answer- unless you've set your datacontext to itself in the codebehind, you'll need to do in the xaml itself- define x:name and use ElementName in the binding definition-
ItemsSource="{Binding ItemsById,ElementName=_this}"...

From what I can see in your example you are using DependencyProperty for your items in your Window. ComboBox is designed to work with collections so let's use it in MvvM (Model-View-ViewModel).
ViewModel.cs:
private List<int> _cmbList;
public List<int> CmbList//this is the collection that we will use to hold values
{
get { return _cmbList; }
set { _cmbList = value; OnPropertyChanged("CmbList"); }
}
//with additional property
private int _ActRma;
public int ActRma
{
get { return _ActRma; }
set { _ActRma = value; OnPropertyChanged(nameof(ActRma)); }// In here you can handle the changed value of your ComboBox. No need for event handler in this case as setter is called everytime you change selected item in UI.
}
Your view model is then referenced in your MainWindow.xaml:
xmlns:vm="clr-namespace:VM;assembly=VM"
This then should be setting the DataContext of your MainWindow.xaml like so:
<Window.DataContext>
<vm:MainViewModel/>
</Window.DataContext>
At this point your window and all of the elements declared in it will inherit the DataContext object which is our ViewModel.
Then to use the Collection in our ComboBox you can use it like this:
<ComboBox ItemsSource="{Binding CmbItems}" SelectedItem="{Binding ActRmaId}" AlternationCount="1"/><!-- here we hold the Id of the selected item, we don't need to set the Display path as we are binding to the int and this being primitive type doesn't have any properties! -->

Related

C# WPF ICollectionView Filter not firing on custom UserControl

So I have developed an editable combobox with a checkbox drop down list. The user can type in the textbox section of the combobox and it should filter the items. But I'm having an issue with the filter not firing when the FilterText is changed.
This is my Usercontrol XAML. All code is simplified.
<ComboBox x:Name="cmbBox" IsEditable="True" IsReadOnly="False"
ItemsSource="{Binding FilteredItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Text="{Binding FilterText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsDropDownOpen="{Binding DropDownOpen, Mode=TwoWay}" >
Code behind looks like this:
public ICollectionView FilteredItems
{
get { return (ICollectionView)GetValue(FilteredItemsProperty); }
set { SetValue(FilteredItemsProperty, value); }
}
public static readonly DependencyProperty FilteredItemsProperty =
DependencyProperty.Register("FilteredItems", typeof(ICollectionView), typeof(ComboCheckBox), new PropertyMetadata(null));
public string FilterText
{
get { return (string)GetValue(FilterTextProperty); }
set { SetValue(FilterTextProperty, value); }
}
public static readonly DependencyProperty FilterTextProperty =
DependencyProperty.Register("FilterText", typeof(string), typeof(ComboCheckBox), new PropertyMetadata(null));
public ObservableCollection<Item> mItems
{
get { return (ObservableCollection<Item>)GetValue(mItemsProperty); }
set { SetValue(mItemsProperty, value); }
}
public static readonly DependencyProperty mItemsProperty =
DependencyProperty.Register("mItems", typeof(ObservableCollection<Item>), typeof(ComboCheckBox), new PropertyMetadata(null));
public ComboCheckBox()
{
InitializeComponent();
FilteredItems = CollectionViewSource.GetDefaultView(mItems);
// apply filtering delegate
FilteredItems.Filter = i =>
{
// This will be invoked for every item in the underlying collection every time Refresh is invoked
if (string.IsNullOrEmpty(FilterText)) return true;
Item m = (Item)i;
return m.Name.ToLower().Contains(FilterText.ToLower());
};
}
So in my constructor I apply the filter delegate. And below is my MainWindow XAML:
<Window.DataContext>
<local:MainWindowsVM />
</Window.DataContext>
<Grid>
<dd:ComboCheckBox x:Name="combo" Width="400" Height="30" FilteredItems="{Binding DataSource}" mItems="{Binding OC}" />
</Grid>
And the ViewModel:
private ObservableCollection<Item> _oc;
public ObservableCollection<Item> OC
{
get { return _oc; }
set
{
_oc = value;
OnPropertyChanged("OC");
}
}
private ICollectionView _datasouorce;
public ICollectionView DataSource
{
get { return _datasouorce; }
set
{
_datasouorce = value;
OnPropertyChanged("DataSource");
}
}
public MainWindowsVM()
{
oc = new ObservableCollection<Item>();
oc.Add(new Item { Name = "ACT", IsChecked = false });
oc Add(new Item { Name = "ADVICENNE", IsChecked = false });
oc.Add(new Item { Name = "NOVARTIS", IsChecked = false });
DataSource = CollectionViewSource.GetDefaultView(oc);
}
With the above code I'm able to successfully view the items in my drop down list which tells me that the DataSource was bound to FilteredItems correctly on my MainWindow, but for some reason when typing in the textbox no filter occurs. If however I place the filter delegate in the Main Window VM then it works, but I want this logic in the Usercontrol codebehind, not in the MainWindow.
Any advice is very much appreciated.

DropDown bind to property names; based on first set second DropDown binding

I have one ObservableCollection<M> fooBar {get;set;}. The class M.cs looks like this:
public class M{
private int _ID;
public int ID {
get {return this._ID;}
set {this._ID = value;}
}
private string _number;
public int Number {
get {return this._number;}
set {this._number = value;}
}
private string _power;
public int Power {
get {return this._power;}
set {this._power = value;}
}
/*
...
*/
}
Now I want to bind the names of these 3 propertys to a ComboBox. I don't want to do it like this:
<ComboBox>
<ComboBoxItem>ID</ComboBoxItem>
<ComboBoxItem>Number</ComboBoxItem>
<ComboBoxItem>Power</ComboBoxItem>
</ComboBox>
Is there a more comfortable way?
Based on the choose of the first ComboBox I want to fill the second ComboBox. As example I choose in the first ComboBox the property Numberthen the second ComboBox should look like this
<ComboBox
SelectedValue="{Binding ???}"
ItemsSource="{Binding fooBar}"
SelectedValuePath="Number"
DisplayMemberPath="Number"
/>
Maybe someone of you can help me, because I have no idea how to connect both comboboxes.
This is how I would do it:
Make a property on the view model which exposes the properties on the model (class M) which can be selected. This way you explicitly control which properties can be selected.
Make a property to hold the selected value of each combo box.
DisplayMemberPath/SelectedValuePath in ComboBox2 binds to the SelectedValue of ComboBox1.
ViewModel:
// ComboBox 1
public Dictionary<string, string> SelectableProperties = new Dictionary<string, string>()
{
{ nameof (M.ID), "ID" }
{ nameof (M.Number), "Nummer" }
{ nameof (M.Power), "Potenz" }
}
// Selection in combobox 1 (not strictly necessary as it can be handled in view, but you may need to know what SelectedValue represents)
private string _selectedValueMember = String.Empty;
public string SelectedValueMember
{
get { return _selectedValueMember; }
set { _selectedValueMember = value; }
}
// Selection in combobox 2 (object just in case there could be other values than int)
private object _selectedValue = null;
public object SelectedValue
{
get { return _selectedValue; }
set { _selectedValue = value; }
}
public ObservableCollection<M> FooBar{ get; set; }
View:
<ComboBox x:Name="ComboBox1"
Width="100"
Margin="5"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedValue="{Binding SelectedValueMember}"
ItemsSource="{Binding SelectableProperties}">
</ComboBox>
<ComboBox Width="100"
Margin="5"
DisplayMemberPath="{Binding ElementName=ComboBox1, Path=SelectedValue}"
SelectedValuePath="{Binding ElementName=ComboBox1, Path=SelectedValue}"
SelectedValue="{Binding SelectedValue}"
ItemsSource="{Binding FooBar}">
</ComboBox>
For the 1st ComboBox: Use Reflection to get the names of all the properties of class M and put those into the ComboBox.
For the 2nd ComboBox: When selection changes in the 1st one, you get the property name. Now set the SelectedValue binding to the property that was selected in the 1st ComboBox.

WPF binding to an already bound property

What can I do if I want to bind to some property which is already bound to something else?
In my case, I got a window which has a TextBox. The Text property of this TextBox is data bound to a combo box to it's selectedItem. My Window class got a public string property which I want to update with whatever value is in the TextBox so I wanted to data bind with the Text property but as I said, it's already bound.
How can I update my property with the text in TextBox? Must it use a routed event of TextChanged or can I do it via Xaml?
Also specifically with properties you define them yourself in your window.cs ... how can you bind them to the TextBox.Text? I tried doing it with the <Window> declaration, meaning <Window MyProperty={Binding ...} /> but the property is not recognized there. Why and how do I do it?
You could solve this easily using the MVVM pattern.
ViewModel:
public class ChooseCategoryViewModel : ViewModelBase
{
private string[] _categories =
{ "Fruit", "Meat", "Vegetable", "Cereal" };
public string[] Categories
{
get { return _categories; }
}
private string _selectedCategory;
public string SelectedCategory
{
get { return _selectedCategory; }
set
{
_selectedCategory = value;
OnPropertyChanged("SelectedCategory");
if (value != null && CategoryName != value)
CategoryName = value;
}
}
private string _categoryName;
public string CategoryName
{
get { return _categoryName; }
set
{
_categoryName = value;
OnPropertyChanged("CategoryName");
if (Categories.Contains(value))
{
SelectedCategory = value;
}
else
{
SelectedCategory = null;
}
}
}
}
XAML:
<ComboBox ItemsSource="{Binding Categories}"
SelectedItem="{Binding SelectedCategory}" />
<TextBox Text="{Binding CategoryName}" />

Combo box binding in mvvm

I am not able to find out Why is combo box binding not working?
I have a view model which looks like (2 properties)
public ProcessMaintenanceDataObject CurrentProcess
{
get
{
return _CurrentProcess;
}
set
{
_CurrentProcess = value;
OnPropertyChanged("CurrentProcess");
}
}
public ObservableCollection<ProcessMaintenanceDataObject > Processes
{
get
{
return _Processes;
}
set
{
_Processes = value;
OnPropertyChanged("Processes");
}
}
public ObservableCollection<FolderInfo> Folders
{
get
{
return _folders;
}
set
{
_folders = value;
OnPropertyChanged("Folders");
}
}
The following is the ProcessMaintenanceDataObject definition
[DataMember]
public string ProcessName
{
get
{
return _ProcessName;
}
set
{
_ProcessName = value;
OnPropertyChanged("ProcessName");
}
}
[DataMember]
public string Id
{
get
{
return _Id;
}
set
{
_Id = value;
OnPropertyChanged("Id");
}
}
[DataMember]
public string FolderId
{
get
{
return _FolderId;
}
set
{
_FolderId = value;
OnPropertyChanged("FolderId");
}
}
[DataMember]
public FolderInfo Folder
{
get
{
return _Folder;
}
set
{
_Folder = value;
if (_Folder != null)
FolderId = _Folder.FolderId;
OnPropertyChanged("Folder");
}
}
The FolderInfo class has FolderName and FolderId Property.
I have a method in viewmodel which fills the Processes.
In my view I have structure something like, I have a treeview which will be bound to Processes and while selecting any of the item from the treeview, i need to allow user to edit that entity.
In the view the combo box binding is as:
<ComboBox ItemsSource="{Binding Path=Folders, Mode=OneWay}"
DisplayMemberPath="FolderName"
SelectedItem="{Binding Source={StaticResource viewModel}, Path=CurrentProcess.Folder, Mode=TwoWay}">
...
This binding doesn't work I mean when I select any object from the tree it fills other information like ProcesName in the textBox but it doesn't make the Folder object as the selected item in combobox, however the combo box will be filled.
Any suggestion.
Do refer this:
If you want to bind a ComboBox to your folders property in two way mode with edit support,
Then you should define data template for your combo box and then bind properties of FolderInfo class to those text boxes
Binding display member path will not solve your problem
I'd suggest you to change DisplayMemberPath with appropriate DataTemplate:
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FolderName}">
</StackPanel>
<DataTemplate>
This will fix SelectedItem context.
Maybe just maybe, your Folderinfo class is not a notificationObject. If it is the case make sure it implements INotifyPropertyChange.
you have to use SelectedValuePath and SelectedValue instead of SelectedItem like below,
<ComboBox ItemsSource="{Binding Path=Folders, Mode=OneWay}"
DisplayMemberPath="FolderName"
SelectedValuePath="FolderId"
SelectedValue="{Binding Path=FolderId, Mode=TwoWay}">
SelectedItem binds the whole object whereas SelectedValue binds only particular properties of the object.

Notify viewmodel that model has changed (from combobox)

I have one model that implements INotifyPropertyChanged through BaseModel class.
It has other model as element inside of it.
class SIDPoslJavnaUstanova : BaseModel
{
private int? _sid_posl_javna_ustanova_id;
...
private decimal? _udaljenost;
private SIDJavnaUstanova _sid_javna_ustanova;
public SIDJavnaUstanova SidJavnaUstanova
{
get { return _sid_javna_ustanova; }
set {
if (_sid_javna_ustanova != value)
{
_sid_javna_ustanova = value;
if (_sid_javna_ustanova != null)
{
_sid_javna_ustanova_id = _sid_javna_ustanova.SidJavnaUstanovaId;
}
else
{
_sid_javna_ustanova_id = null;
}
RaisePropertyChanged("SidJavnaUstanova");
}
}
}
I have viewmodel that has observable collection of this model objects.
class BaseViewModel<T> : ObservableObject
{
private ObservableCollection<T> _elements = new ObservableCollection<T>();
public ObservableCollection<T> Elements
...
class SIDPoslJavnaUstanovaViewModel : BaseViewModel<SIDPoslJavnaUstanova>
{
}
}
And finally, mainviewmodel that is bound to view:
class MainViewModel : BaseViewModel<Store>
{
private SIDJavnaUstanovaViewModel _sidJavnaUstanovaViewModel;
private SIDJavnaUstanova _sidJavnaUstanova;
public SIDPoslJavnaUstanovaViewModel SidPoslJavnaUstanovaViewModel
{
get { return _sidPoslJavnaUstanovaViewModel; }
set
{
if (_sidPoslJavnaUstanovaViewModel != value)
{
_sidPoslJavnaUstanovaViewModel = value;
RaisePropertyChanged("SidPoslJavnaUstanovaViewModel");
}
}
}
public SIDJavnaUstanovaViewModel SidJavnaUstanovaViewModel
{
get { return _sidJavnaUstanovaViewModel; }
set
{
if (_sidJavnaUstanovaViewModel != value)
{
_sidJavnaUstanovaViewModel = value;
RaisePropertyChanged("SidJavnaUstanovaViewModel");
}
}
}
SidJavnaUstanova is only used to populate combobox, and to bind to object when choosen.
I have combobox in datagrid, that has mulitple lines. Element is SIDJAVNAUSTANOVA , and dropdown is SIDJAVNAUSTANOVAVIEWMODEL.
Dropdown is SIDJAVNAUSTANOVAVIEWMODEL.ELEMENTS
(cannot show you picture not enough reputation)
<src:BaseWindow.Resources>
<viewmod:MainViewModel x:Key="StoreViewM"/>
</src:BaseWindow.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Width="140" Header="{StaticResource name}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=SidJavnaUstanovaViewModel.Elements,
Source={StaticResource StoreViewM}}"
SelectedItem="{Binding Path=SidJavnaUstanova,UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay}"
DisplayMemberPath="Naziv"
SelectedValue="{Binding Path=SidJavnaUstanova, Mode=TwoWay}">
</ComboBox>
...
Everything is working fine except when combobox is changed, element SIDJavnaUstanova of object SIDPoslJavnaUstanova is changed, and I can catch this in its model property. But what I must have, is to catch change of this SidJavnaUstanova in viewmodel, so I can implement check-out if there are duplicates of sidjavnaustanova in sidposljavnaustanovaviewmodel.elements. I cannot realize how to do that.
Something like
SIDPoslJavnaUstanova.Elements.??? SIDJavnaUstanova
I cannot do this because elements is observable collection.
Maybe it is a bad model, please suggest something or help with current code.
You need to a) specify source for SelectedItem b) bind SelectedItem to the property of the same type, as elements in your collection (i.e. SIDPoslJavnaUstanova in your case).
This should work, i guess:
<ComboBox ItemsSource="{Binding Path=SidJavnaUstanovaViewModel.Elements,
Source={StaticResource StoreViewM}}"
SelectedItem="{Binding Path=SelectedModel,UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, Source={StaticResource StoreViewM}}"
DisplayMemberPath="Naziv">
</ComboBox>
.........................................
//MainViewModel
public SIDPoslJavnaUstanova SelectedModel
{
get { return _selectedModel; }
set
{
if (_selectedModel != value)
{
_selectedModel = value;
RaisePropertyChanged("SelectedModel");
}
}
}
And yes, this is some awful design.

Categories

Resources