WPF ComboBox with CheckBoxes display info about checked items? - c#

Im trying to make a ComboBox that have checkboxes as items and based on what is checked display different things when the combobox is "closed".
The look Im trying achieve can be seen in the image.
Ideally I dont want the user to be able to select the text in the top ( in the image).
Is there a simple solution to this? I've seen solutions where one can display more information when all the items are shown by using DataTriggers to hide different nested controls, but that is not really what Im looking for.
Any ideas?
/Erik

Here is a way to achieve most of what you want using a ComboBox, except that the text can still be selected (using custom text only works when IsEditable is true). It is not editable though because of IsReadOnly="true".
View
<ComboBox
IsEditable="True"
IsReadOnly="True"
ItemsSource="{Binding Items}"
Text="{Binding Text}">
<ComboBox.ItemTemplate>
<DataTemplate
DataType="{x:Type local:Item}">
<CheckBox
Content="{Binding Name}"
IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Viewmodel
// ObservableObject is a custom base class that implements INotifyPropertyChanged
internal class MainWindowVM : ObservableObject
{
private ObservableCollection<Item> mItems;
private HashSet<Item> mCheckedItems;
public IEnumerable<Item> Items { get { return mItems; } }
public string Text
{
get { return _text; }
set { Set(ref _text, value); }
}
private string _text;
public MainWindowVM()
{
mItems = new ObservableCollection<Item>();
mCheckedItems = new HashSet<Item>();
mItems.CollectionChanged += Items_CollectionChanged;
// Adding test data
for (int i = 0; i < 10; ++i)
{
mItems.Add(new Item(string.Format("Item {0}", i.ToString("00"))));
}
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
{
foreach (Item item in e.OldItems)
{
item.PropertyChanged -= Item_PropertyChanged;
mCheckedItems.Remove(item);
}
}
if (e.NewItems != null)
{
foreach (Item item in e.NewItems)
{
item.PropertyChanged += Item_PropertyChanged;
if (item.IsChecked) mCheckedItems.Add(item);
}
}
UpdateText();
}
private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsChecked")
{
Item item = (Item)sender;
if (item.IsChecked)
{
mCheckedItems.Add(item);
}
else
{
mCheckedItems.Remove(item);
}
UpdateText();
}
}
private void UpdateText()
{
switch (mCheckedItems.Count)
{
case 0:
Text = "<none>";
break;
case 1:
Text = mCheckedItems.First().Name;
break;
default:
Text = "<multiple>";
break;
}
}
}
// Test item class
// Test item class
internal class Item : ObservableObject
{
public string Name { get; private set; }
public bool IsChecked
{
get { return _isChecked; }
set { Set(ref _isChecked, value); }
}
private bool _isChecked;
public Item(string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
}
If the selectable text is an issue, you may want to create a custom ComboBox control template (default example here). Alternatively, you could use something else instead of a ComboBox, and make it look like a ComboBox.
Screenshot of example:

Using a combination of the #Erik83 and #Xavier solution I still had the problem that selecting a ComboBoxItem in a location right from the CheckBox text closes the ComboBox-DropDown and shows the ToString() value of the ComboBoxItem as the CheckBox is not stretched to DropDown-Width. I solved it by adding
HorizontalContentAlignment="Stretch"
to the CheckBox and adding the ItemContainerStyle:
<ComboBox x:Name="combobox"
Background="White"
Padding="2"
Text="{Binding ElementName=DockPanelTemplateComboCheck, Path=ComboTextFilter}"
IsEditable="True"
IsReadOnly="True"
HorizontalAlignment="Stretch"
ItemsSource="{Binding ...}"
IsDropDownOpen="{Binding Path=DropOpen, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, UpdateSourceTrigger=PropertyChanged}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Eintrag}" HorizontalContentAlignment="Stretch" Checked="CheckBox_Checked_Unchecked" Unchecked="CheckBox_Checked_Unchecked"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
CodeBehind:
private string combotextfilter = "<No Selection>";
public string ComboTextFilter
{
get { return combotextfilter; }
set
{
if (value != null && value.IndexOf("ComboModel") != -1) return;
combotextfilter = value;
NotifyPropertyChanged(nameof(ComboTextFilter));
}
}
private void CheckBox_Checked_Unchecked(object sender, RoutedEventArgs e)
{
switch (((ObservableCollection<ComboModel>)combobox.ItemsSource).Count(x => x.IsChecked))
{
case 0:
ComboTextFilter = "<No Selection>";
break;
case 1:
ComboTextFilter = ((ObservableCollection<ComboModel>)combobox.ItemsSource).Where(x => x.IsChecked).First().Eintrag;
break;
default:
ComboTextFilter = ((ObservableCollection<ComboModel>)combobox.ItemsSource).Where(x => x.IsChecked).Select(x => x.Eintrag).Aggregate((i, j) => i + " | " + j);
//ComboTextFilter = "<Multiple Selected>";
break;
}
NotifyPropertyChanged(nameof(C_Foreground));
}
public bool DropOpen
{
get { return dropopen; }
set { dropopen = value; NotifyPropertyChanged(nameof(ComboTextFilter)); }
}
private bool dropopen = false;

#Xaviers answer works 99% of the way. However the user is able to accidently select a checkbox and then the ToString() of the checkbox is shown as the selected text. This can happen quite alot actually. I havent yet worked out why this happens, but I've found a way to prevent this.
Create a bool property that binds to the DropDownOpen property of the combobox and when the DropDownOpen has changed to false it means that the DropDown has just been closed and you might be facing the above problem.
So here you just raise the propertychanged event for the viewmodel and pass the property bound to the Text property of the combobox.

Related

Trouble binding to property in uwp MVVM project

I am having a problem with binding from a view to a Viewmodel property.(UWP)
<AppBarToggleButton Label="Active" Icon="People"
IsChecked="{x:Bind ViewModel.IsStatusBtnChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Click="{x:Bind ViewModel.empStautsBtnClicked}"/>
private bool isStatusBtnChecked = true;
public bool IsStatusBtnChecked
{
get { return isStatusBtnChecked; }
set { Set(ref isStatusBtnChecked, value); }
}
When I try to get the value from a method to load combobox items the value is allways the default value
private List<string> depcombo;
public List<string> Depcombo
{
get { return depcombo; }
set
{
if (depcombo != value)
{
depcombo = value;
OnPropertyChanged("Depcombo");
}
}
}
public async void GetDepCombo()
{
List<string> _dep = new List<string>();
var data2 = await SqlServerDataService.GetAllEmployeesAsync();
var depResult = (from emp in EmpItems
where emp.Status == IsStatusBtnChecked
select emp.Department).Distinct();
foreach (var item in depResult)
{
if (item != null)
{
_dep.Add(item);
}
}
Depcombo = _dep;
}
When I load the data for Employyes it works fine
public async Task LoadDataAsync(MasterDetailsViewState viewState)
{
EmpItems.Clear();
var data = await SqlServerDataService.GetAllEmployeesAsync();
data.Where(em => em.Status == IsStatusBtnChecked).ToList().ForEach(p => EmpItems.Add(p));
if (viewState == MasterDetailsViewState.Both)
{
Selected = EmpItems.FirstOrDefault();
}
}
Some help will be much appreciated
When I try to get the value from a method to load combobox items the value is allways the default value
It's is confused that which is the combobox ItemsSource, if Depcombo is ComboBox ItemsSource, You have passed a new list instance to ItemsSource when you call GetDepCombo method.
Depcombo = _dep;
So, we need to set bind mode as OneWay(OneTime is default) that could response the object instance change.
<ComboBox
Margin="0,120,0,0"
ItemsSource="{x:Bind MainViewModel.Depcombo, Mode=OneWay}"
>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If EmpItems is ComboBox ItemsSource, and it is ObservableCollection type. When you call LoadDataAsync, EmpItems clear the items fist, then add the new items. And this processing does not change the EmpItems instance object. it could works in onetime mode.
<ComboBox Margin="0,120,0,0" ItemsSource="{x:Bind MainViewModel.EmpItems}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<AppBarToggleButton Label="Active" Icon="People"
x:Name="empStatusBtn"
IsChecked="{x:Bind ViewModel.IsStatusBtnChecked, Mode=TwoWay}"
Click="{x:Bind ViewModel.empStautsBtnClicked}"/>
public async void empStautsBtnClicked()
{
await LoadDataAsync(MasterDetailsViewState.Both);
}
Looks like the problem is when the view is reloaded. Is there a way to refresh the view without reloading. When reloading the value of
private bool isStatusBtnChecked = true;
public bool IsStatusBtnChecked
{
get { return isStatusBtnChecked; }
set
{
if (isStatusBtnChecked != value)
{
isStatusBtnChecked = value;
OnPropertyChanged("IsStatusBtnChecked");
}
}
}
is true but the button isChecked property is false;

Maintain checkbox state while the app is running in WPF

I'm working in MVVM, WPF and I have a popup; inside this popup is a listbox and inside the listbox I have a checkbox. The problem is: if I uncheck an item from the list box and click outside, popup disappears; if a I click again the checkbox is reseted at its initial value (all the items become checked).
So, how can I maintain the state of the popup set and stop its resetting while the app is running ? Can I do this through XAML ?
here is the code:
public class CheckedListItem<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isChecked = false;
private T item;
public CheckedListItem()
{ }
public CheckedListItem(T item, bool isChecked)
{
this.item = item;
this.isChecked = isChecked;
}
public T Item
{
get { return item; }
set
{
item = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Item"));
}
}
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
}
the viewModel:
private void OnApplyFiltersCommandRaised(object obj)
{
if (FilterElement.Contains("ClassView"))
{
switch (FilterElement)
{
case "buttonClassViewClassFilter":
FilteredClassViewItems.Clear();
FilteredFieldViewItems.Clear();
foreach (var filterItem in FilterItems)
{
if (filterItem.IsChecked == true)
{
FilteredClassViewItems.Add(classViewItems.First(c => c.ClassName == filterItem.Item));
FilteredFieldViewItems.Add(fieldViewItems.First(c => c.ClassName == filterItem.Item));
}
}
break;
...
public ObservableCollection<CheckedListItem<string>> FilterItems
{
get
{
return filterItems;
}
set
{
filterItems = value;
SetPropertyChanged("FilterItems");
}
}
the XAML part:
<ListBox x:Name="listBoxPopupContent"
Height="250"
ItemsSource="{Binding FilterItems}"
BorderThickness="0"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListBox.Resources>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="FontSize" Value="8" />
<Setter Property="IsSelected" Value="{Binding IsChecked, Mode=TwoWay}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Item}"
Command="{Binding DataContext.ApplyFiltersCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListBox}}}"
CommandParameter="{Binding IsChecked,
RelativeSource={RelativeSource Self},
Mode=OneWay}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Thanks in advance !
If you want to keep the state, you can just create a new view that will contain your listbox. Then your popup will be
<Popup>
<views:MyListBoxview>
</Popup>
where views is the path where wpf can find MyListBoxview.
This is an example of how you can do MyListBoxView. First of all, add a new usercontrol to your project. Then you create:
<ListBox ItemSource = {Binding MyCollectionOfItem}>
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked = {Binding IsItemChecked} Content = {Binding Name}/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You will need to assign to this view a viewmodel that will of course implement INotifyPropertyChanged and that will have these this class defined inside it (also this class will implement INotifyPropertyChanged)
public class MyItem : INotifyPropertyChanged
{
public void SetPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private bool isItemChecked = false;
public bool IsItemChecked
{
get { return isItemChecked; }
set
{
isItemChecked = value;
SetPropertyChanged("IsItemChecked");
}
}
private string name ;
public string Name
{
get { return Name; }
set
{
name = value;
SetPropertyChanged("Name");
}
}
}
finally, the viewmodel that will represent the state of the popup will have inside this property
private ObservableCollection<MyItem> myCollectionOfItem = new ObservableCollection<MyItem>();
public ObservableCollection<MyItem> MyCollectionOfItem
{
get { return myCollectionOfItem; }
set
{
myCollectionOfItem = value;
SetPropertyChanged("MyCollectionOfItem");
}
}
I usually handle this kind of problem modelling properly the object that i need to bind to my controls in WPF

ListBox with DataTemplate recognize SelectedItem

I have a ListBox with a simple DataTemplate, a CheckBox, and a TextBox.
If the user checks a CheckBox I want to get this changed item, like the property SelectedItem of the ListBox.
How can I get the element from List2, which has changed?
MyListItem:
public class MyListItem2 : ReactiveObject
{
private string _name;
public string Name
{
get { return _name; }
set
{
this.RaiseAndSetIfChanged(ref _name, value, "Name");
}
}
private bool _isMarked;
public bool IsMarked
{
get { return _isMarked; }
set
{
this.RaiseAndSetIfChanged(ref _isMarked, value, "IsMarked");
}
}
}
View:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataTemplate.Views.MainWindow"
xmlns:viewsmodels="clr-namespace:DataTemplate.ViewModels;assembly=DataTemplate"
xmlns:dt="clr-namespace:DataTemplate;assembly=DataTemplate"
Title="DataTemplate" Width="700">
<Window.DataContext>
<viewsmodels:MainWindowViewModel />
</Window.DataContext>
<Grid ColumnDefinitions="250">
<ListBox Grid.Column="1" Items="{Binding List2}">
<ListBox.ItemTemplate>
<DataTemplate DataType="dt:MyListItem2">
<Grid ColumnDefinitions="50*,50*">
<CheckBox Grid.Column="0" Content="Mark" IsChecked="{Binding IsMarked}"/>
<TextBox Grid.Column="1" Text="{Binding Name}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
ViewModel:
public class MainWindowViewModel : ReactiveObject
{
public ObservableCollection<MyListItem2> List2 { get; set; }
public MainWindowViewModel()
{
List2 = new ObservableCollection<MyListItem2>();
Random rand = new Random();
for (int i = 0; i < rand.Next(1, 20); i++)
{
MyListItem2 mli = new MyListItem2();
mli.Name = "ListItem" + i;
mli.IsMarked = false;
mli.PropertyChanged += ItemChanged;
List2.Add(mli);
}
}
private void ItemChanged(object sender, PropertyChangedEventArgs e)
{
var item = sender as MyListItem2;
Console.WriteLine(string.Format("changed: {0} {1}", item.Name, item.IsMarked));
}
}
I can see two ways:
Since you are using MVVM, implement the INotifyPropertyChanged interface on the MyListItem2 class (Microsoft Reference on INotifyPropertyChanged implementation). Raise the property change event when the IsMarked value is set/changed, then wire into the PropertyChanged event handler of the item to determine when it is changed. . OR
If you have codebehidn, add a "Checked" and/or "Unchecked" event handler on the checkbox itself from the XAML. Shown below.
CheckBox Grid.Column="0" Content="Mark" IsChecked="{Binding IsMarked}"/>
Checked="IsMarked_Checked"
Codebehind
public void IsMarked_Checked(object sender, RoutedEventArgs e)
{
var ck = sender As Checkbox;
if (ck == null)
{
return;
}
// do whatever you need to here using the datacontext of the Checkbox
}
If you want to know when a check box is checked/unchecked by the user you will need to trigger on the event from the checkbox.
Use something like this:
private void MyCheckBox_Checked(object sender, RoutedEventArgs e)
{
//check IsChecked of MyCheckBox here
}
Try setting binding Mode:
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"

Binding happened also by changing the values from the keyboard

I have a combobox that is bind to the following list:
private List<string> strList;
public List<string> StrList
{
get { return strList; }
set
{
strList = value;
OnPropertyChanged("StrList");
}
}
The selected item is bind to the next object:
private string str;
public string Str
{
get { return str; }
set
{
if (str != value)
{
str = value;
OnPropertyChanged("Str");
}
}
}
Following the combobox:
<ComboBox ItemsSource="{Binding StrList}"
SelectedItem="{Binding Str,UpdateSourceTrigger=LostFocus}"
Height="50" Width="200"/>
I want that the binding happens only on lost focus, and when changing the values ​​with the keys of the keyboard.
Therefore UpdateSourceTrigger=LostFocus.
My question is how to do that binding happened also by changing the values ​​from the keyboard?
I created a behavior and in it I renewed the binding in a case of pressing the keys:
public class KeysChangedBehavior : Behavior<ComboBox>
{
protected override void OnAttached()
{
this.AssociatedObject.AddHandler(ComboBox.KeyDownEvent,
new RoutedEventHandler(this.OnKeysChanged));
this.AssociatedObject.AddHandler(ComboBox.KeyUpEvent,
new RoutedEventHandler(this.OnKeysChanged));
}
protected void OnKeysChanged(object sender, RoutedEventArgs e)
{
BindingExpression _binding = ((ComboBox)sender).GetBindingExpression(ComboBox.SelectedItemProperty);
if (_binding != null)
_binding.UpdateSource();
}
}
Here the combobox:
<ComboBox ItemsSource="{Binding StrList}" SelectedItem="{Binding Str,UpdateSourceTrigger=LostFocus}" Height="50" Width="200">
<i:Interaction.Behaviors>
<KeysChangedBehavior/>
</i:Interaction.Behaviors>
</ComboBox>

Validating bound ObservableCollection in ViewModel using MVVM Pattern

I'm new to MVVM, just recently started my first project following the MVVM pattern. I have an issue trying to validate an ObservableCollection using the IDataErrorInfo Interface. My ObservableCollection looks like this:
ObservableCollection<Magazine> magazineRepository;
public ObservableCollection<Magazine> MagazineRepository
{
get { return magazineRepository; }
set
{
if (value != null)
{
bladRepository = value;
OnPropertyChanged("MagazineRepository");
}
}
}
And my XAML like this:
<ListBox x:Name="listMagazineRepository"
Grid.ColumnSpan="2"
ItemsSource="{Binding}"
DataContext="{Binding MagazineRepository}"
DisplayMemberPath="Navn"
SelectedItem="{Binding Path=SelectedItem}"/>
<TextBox x:Name="txtName" Grid.Row="1" Grid.Column="0"
Text="{Binding ElementName=listMagazineRepository, Path=SelectedItem.Navn, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<TextBox x:Name="txtPrice" Grid.Row="2" Grid.Column="0"
Text="{Binding ElementName=listMagazineRepository, Path=SelectedItem.Pris, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
It's just a simple listBox containing objects, when you select an item, the selected objects properties is displayed in the textboxes, and is then bound to the listbox object.
My Problem is, that when I set my code up like this, the only way I can figure out how to validate my data is in the Domain Model, which really isn't a good practise, I'd like to validate in the ViewModel before it gets there. Basically I want to validate each property in the MagazineRepository, in the ViewModel, How would you go about doing this?
PS: I'm new to posting on this board (and programming boards in general) if my question is lacking information, please let me know and I will supply the needed details.
Thanks a lot.
If i understand correctly you want to validate the Magazine object. If that's the case, one way to do it is to wrap that class in a viewmodel, let's call it MagazineVM, that implements IDataErrorInfo and keep the magazine object updated. You then bind to the view a list of MagazineVM. As a very simple example:
public class MagazineVM : IDataErrorInfo, INotifyPropertyChanged
{
private Magazine _magazine;
public int FirstMagazineProperty
{
get { return _magazine.FirstMagazineProperty; }
set { _magazine.FirstMagazineProperty = value; RaisePropertyChanged("FirstMagazineProperty"); }
}
//INotifyPropertyChanged implementation
//IDataErrorInfo implementation
}
Firstly, as Dtex says, you should use a MagazineViewModel class rather than a Magazine class. E.G.
public class MagazineViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private string navn;
private string pris;
private string error;
public string Navn
{
get { return navn; }
set
{
if (navn != value)
{
navn = value;
RaisePropertyChanged("Navn");
}
}
}
public string Pris
{
get { return pris; }
set
{
if (pris != value)
{
pris = value;
RaisePropertyChanged("Pris");
}
}
}
public string Error
{
get { return error; }
set
{
if (error != value)
{
error = value;
RaisePropertyChanged("Error");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public string this[string columnName]
{
get
{
var result = string.Empty;
switch (columnName)
{
case "Pris":
if (string.IsNullOrWhiteSpace(Pris))
{
result = "Pris is required";
}
break;
case "Navn":
if (string.IsNullOrWhiteSpace(Navn))
{
result = "Navn is required";
}
break;
}
return result;
}
}
private void RaisePropertyChanged(string PropertyName)
{
var e = PropertyChanged;
if (e != null)
{
e(this, new PropertyChangedEventArgs(PropertyName));
}
}
}
The important property to note is "public string this[string columnName]". ColumnName will be one of your bound properties and this is where you can do validation.
The next thing to consider is your MainViewModel (Your DataContext). E.G.
public class MainViewModel : INotifyPropertyChanged
{
//Use a readonly observable collection. If you need to reset it use the .Clear() method
private readonly ObservableCollection<MagazineViewModel> magazines = new ObservableCollection<MagazineViewModel>();
private MagazineViewModel selectedItem;
//Keep the item being edited separate to the selected item
private MagazineViewModel itemToEdit;
public ObservableCollection<MagazineViewModel> Magazines { get { return magazines; } }
public MagazineViewModel SelectedItem
{
get { return selectedItem; }
set
{
if (selectedItem != value)
{
selectedItem = value;
RaisePropertyChanged("SelectedItem");
//When the selected item changes. Copy it to the ItemToEdit
//This keeps the the copy you are editing separate meaning that invalid data isn't committed back to your original view model
//You will have to copy the changes back to your original view model at some stage)
ItemToEdit = Copy(SelectedItem);
}
}
}
public MagazineViewModel ItemToEdit
{
get { return itemToEdit; }
set
{
if (itemToEdit != value)
{
itemToEdit = value;
RaisePropertyChanged("ItemToEdit");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public MainViewModel()
{
//Ctor...
}
//Create a copy of a MagazineViewModel
private MagazineViewModel Copy(MagazineViewModel ToCopy)
{
var vm = new MagazineViewModel();
vm.Navn = ToCopy.Navn;
vm.Pris = ToCopy.Pris;
return vm;
}
private void RaisePropertyChanged(string PropertyName)
{
//...
}
}
The only thing missing here is how you copy the changes back to the original view model. You could do it before the selected item changes (if the ItemToEdit is valid) or have a Commit button that is only enabled when the ItemToEdit is valid. If you can allow your original view models to go into an invalid state you don't need to worry about the copying.
Finally the XAML
An implicit style to show the error tooltip
<Style
TargetType="{x:Type TextBox}">
<Setter
Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Style>
And the controls and bindings
<ListBox
ItemsSource="{Binding Magazines}"
DisplayMemberPath="Navn"
SelectedItem="{Binding Path=SelectedItem, Mode=TwoWay}" />
<TextBox
Margin="5"
x:Name="txtName"
Grid.Row="1"
Grid.Column="0"
Text="{Binding ItemToEdit.Navn, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<TextBox
Margin="5"
x:Name="txtPrice"
Grid.Row="2"
Grid.Column="0"
Text="{Binding ItemToEdit.Pris, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
The TextBoxes bind to ItemToEdit. ItemToEdit will be an in-sync copy of the SelectedItem.

Categories

Resources