Say I have a few radio buttons grouped together in my view.xaml:
<RadioButton GroupName="Group" Content="Item1" Command="{Binding ChangeRadioSelectionCommand}" CommandParameter="Item1" />
<RadioButton GroupName="Group" Content="Item2" Command="{Binding ChangeRadioSelectionCommand}" CommandParameter="Item2" />
<RadioButton GroupName="Group" Content="Item3" Command="{Binding ChangeRadioSelectionCommand}" CommandParameter="Item3" />
Then in my viewmodel.cs I have something like:
public class ViewModel : BindableBase
{
private string radioSelection = "Item1";
public string RadioSelection
{
get { return this.radioSelection; }
set { SetProperty(ref this.radioSelection, value); }
}
public ViewModel()
{
this.ChangeRadioSelectionCommand = new DelegateCommand<string>(this.OnChangeRadioSelection, this.CanChangeRadioSelection);
}
public ICommand ChangeRadioSelectionCommand { get; private set; }
private void OnChangeRadioSelection(string radioSelection)
{
RadioSelection = radioSelection;
}
private bool CanChangeRadioSelection(string radioSelection) { return true; }
}
This works fine for getting values from the view into the viewmodel, but how would I go from the viewmodel to the view if something changes in the viewmodel. For simplicity, let's say I add a button to the xaml:
<Button Command="{Binding ResetRadioSelectionCommand}" />
All it would do is reset the radio selection to the first item and so the viewmodel.cs would look something like:
public class ViewModel : BindableBase
{
private string radioSelection = "Item1";
public string RadioSelection
{
get { return this.radioSelection; }
set { SetProperty(ref this.radioSelection, value); }
}
public ViewModel()
{
this.ChangeRadioSelectionCommand = new DelegateCommand<string>(this.OnChangeRadioSelection, this.CanChangeRadioSelection);
this.ResetRadioSelectionCommand = new DelegateCommand(this.OnResetRadioSelection, this.CanResetRadioSelection);
}
public ICommand ChangeRadioSelectionCommand { get; private set; }
private void OnChangeRadioSelection(string radioSelection)
{
RadioSelection = radioSelection;
}
private bool CanChangeRadioSelection(string radioSelection) { return true; }
public ICommand ResetRadioSelectionCommand { get; private set; }
private void OnResetRadioSelection()
{
RadioSelection = "Item1";
}
private bool CanResetRadioSelection() { return true; }
}
This would change radioSelection, but it won't reflect in the gui. Is there a way to do this? Or perhaps just a better way to deal with radio buttons in general?
It is completely the wrong way. Your ViewModel should contain a sensible property with sensible name. For example, CurrentMode.
FIRST SOLUTION
ViewModel
public enum DisplayMode { Vertical, Horizontal, Diagonal }
private DisplayMode currentMode;
public DisplayMode CurrentMode
{
get { return currentMode; }
set { SetProperty(ref currentMode, value); }
}
And now you can bind this property to RadioButton.IsChecked via IValueConverter:
<RadioButton GroupName="Group" Content="Vertical" IsChecked="{Binding CurrentMode, Converter={StaticResource enumToBoolConverter}, ConverterParameter=Vertical}" />
<RadioButton GroupName="Group" Content="Horizontal" IsChecked="{Binding CurrentMode, Converter={StaticResource enumToBoolConverter}, ConverterParameter=Horizontal}" />
<RadioButton GroupName="Group" Content="Diagonal" IsChecked="{Binding CurrentMode, Converter={StaticResource enumToBoolConverter}, ConverterParameter=Diagonal}" />
Converter is generic for all enums. You need to add it to your project and declare in resource-block of your view.
public class EnumBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object parameterValue = Enum.Parse(value.GetType(), parameterString);
return parameterValue.Equals(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string parameterString = parameter as string;
if (parameterString == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, parameterString);
}
#endregion
}
It's one of many solutions. You may not want to us enum for your property because the subject area is not mapped to enumeration of parameters. Then you can bind to text value:
SECOND SOLUTION
ViewModel
private string currentMode;
public string CurrentMode
{
get { return currentMode; }
set { SetProperty(ref currentMode, value); }
}
View
<RadioButton Name="RadioButton1"
GroupName="Group"
Content="Vertical"
IsChecked="{Binding Path=CurrentMode, Converter={StaticResource boolToStringValueConverter}, ConverterParameter=Vertical}" />
<RadioButton Name="RadioButton2"
GroupName="Group"
Content="Horizontal"
IsChecked="{Binding Path=CurrentMode, Converter={StaticResource boolToStringValueConverter}, ConverterParameter=Horizontal}" />
<RadioButton Name="RadioButton3"
GroupName="Group"
Content="Diagonal"
IsChecked="{Binding Path=CurrentMode, Converter={StaticResource boolToStringValueConverter}, ConverterParameter=Diagonal}" />
Converter
public class BooleanToStringValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (System.Convert.ToString(value).Equals(System.Convert.ToString(parameter)))
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (System.Convert.ToBoolean(value))
{
return parameter;
}
return null;
}
}
The general principle is to store meaningful projection of the subject area in ViewModels. There is no many sense if you'll keep store copy of view properties in your ViewModel. RadioSelection is a senseless name and it can't be correlated to model without additional commentaries.
Related
Need some help. Hello everyone. I really have no idea how to update view elements, when property value is updated with my Egzecute method inside of MsgViewModel, called with public ICommand Start. For example, I want to make one button STOP visible, another one START collapsed, when a property Status changes its value, from Stopped to Sending. Please also be noted, that visibility is updated corrctly when property Status is changed with ViewModels constructor by (default on start for me)Status = Models.SendingStatus.Stopped; or Status = Models.SendingStatus.Sending;.
View:
<!--START, to be collapsed-->
<Button Grid.Row="0"
Grid.Column="4"
Background="#80B584"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay,
Converter={StaticResource boolStart}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="START" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
<!--STOP, to be viewed-->
<Button Grid.Row="0"
Background="#FF8A8A"
Visibility="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay,
Converter={StaticResource boolStop}}" Margin="0,145,443.667,-0.333">
<TextBlock Text="STOP" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
ViewModel:
private Models.MsgModel message= new Models.MsgModel (); //model instance
public MsgViewModel() //constructor, by default makes staus "Stopped"
{
Status = Models.SendingStatus.Stopped;
}
public Models.SendingStatus Status
{
get
{
return message.Status;
}
set
{
message.Status = value;
}
}
private ICommand start;
public ICommand Start //command called by START button, supposed to collapse it, and show STOP button
{
get
{
if (start == null)
start = new RelayCommand(
o =>
{
Egzecute();
});
return start;
}
}
public void Egzecute() //method called by the command
{
Status = Models.SendingStatus.Sending;
var openDialog = new Powiadomienie();
openDialog.ShowPowiadomienie(Status.ToString(), "Powiadomienie"); //shows updated SendingStatus, but the View is not updating to it
}
Model:
public enum SendingStatus: byte { Sending, Waiting, Stopped} //enum for Status property
public class MsgModel : INotifyPropertyChanged
private SendingStatus status;
public SendingStatus Status //Status model property
{
get
{
return status;
}
set
{
status = value;
OnPropertyChanged("Status");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propertyNames)
{
if (PropertyChanged != null)
{
foreach (string propertyName in propertyNames)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Converters:
public class BooleanStart : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ViewModels.MsgViewModel mvm = new ViewModels.MsgViewModel();
bool bvalue = (bool)value;
if (mvm.Status == Models.SendingStatus.Sending|| mvm.Status == Models.SendingStatus.Waiting)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanStop : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
ViewModels.MsgViewModel mvm = new ViewModels.MsgViewModel();
bool bvalue = (bool)value;
if (mvm.Status == Models.SendingStatus.Sending|| mvm.Status == Models.SendingStatus.Waiting)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
My question is, how to have updated View after calling the method by command?
Ok, after several hours I figured out my mistake. Construction of Converter was wrong. Binding supposed to be different, and ViewModel updated with property change notification.
Converters:
public class BooleanStart : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Models.SendingStatus sendingStatus = (Models.SendingStatus)value;
if (sendingStatus == Models.SendingStatus.Sending || sendingStatus == Models.SendingStatus.Waiting)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class BooleanStop : IValueConverter //text decoration
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Models.SendingStatus sendingStatus = (Models.SendingStatus)value;
if (sendingStatus == Models.SendingStatus.Sending || sendingStatus == Models.SendingStatus.Waiting)
{
return Visibility.Visible;
}
else
{
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter,
CultureInfo culture)
{
throw new NotImplementedException();
}
}
For binding:
<!--START-->
<Button Grid.Row="0"
Grid.Column="4"
Background="#80B584"
Visibility="{Binding Path=Status, Converter={StaticResource boolStart}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="START" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
<!--STOP-->
<Button Grid.Row="0"
Background="#FF8A8A"
Visibility="{Binding Path=Status, Converter={StaticResource boolStop}}" Margin="0,145,443.667,-0.333"
Command="{Binding Path=Start}">
<TextBlock Text="STOP" TextWrapping="Wrap" TextAlignment="Center"/>
</Button>
ViewModel` method:
public void Egzecue()
{
Status = Models.SendingStatus.Sending;
OnPropertyChanged("Status");
var openDialog = new Powiadomienie();
openDialog.ShowPowiadomienie(Status.ToString(), "Powiadomienie");
}
I found some silimar questions, but these are not exactly what i need.
I want to bound stackpanel "IsEnabled" value to bool "!IsIterrupted" value of my Items. Here is my XAML file:
<ListView ItemsSource="{Binding Path=Items}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<Button Command="{Binding Path=StopThreadCommand, Source={StaticResource viewModel}}" CommandParameter="{Binding Id}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This is how items looks like:
public class ThreadDecorator : BaseThread , INotifyPropertyChanged
{
...
public event PropertyChangedEventHandler PropertyChanged;
private bool _is_interrupted;
public bool IsInterrupted
{
get { return _is_interrupted; }
set
{
_is_interrupted = value;
OnPropertyChanged("IsInterrupted");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
}
And my ViewModel:
public class ThreadsViewModel : DependencyObject
{
private ThreadsModel _model;
public ThreadsModel Model
{
get { return _model; }
set
{
_model = value;
}
}
public ICollectionView Items
{
get { return (ICollectionView)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ICollectionView), typeof(ThreadsViewModel), new PropertyMetadata(null));
public StopThreadCommand StopThreadCommand { get; set; }
public ThreadsViewModel()
{
this.Model = new ThreadsModel();
Items = CollectionViewSource.GetDefaultView(Model.Threads);
this.StopThreadCommand = new StopThreadCommand(this);
}
public void InterruptThread(int id)
{
_model.InterruptThread(id);
}
}
StopThreadCommand:
public class StopThreadCommand : ICommand
{
public ThreadsViewModel ViewModel {get; set;}
public StopThreadCommand(ThreadsViewModel viewModel)
{
this.ViewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
this.ViewModel.InterruptThread((int)parameter);
}
}
When I am clicking on Stop button, IsInterrupted value is changing from false to true, and stackpanel have to become disabled, but UI does not update. Help please!
The default property of Binding is Path, which is a path to a property/sub-property of the DataContext. It's not an arbitrary C# expression. So you're setting Binding.Path to "!IsInterrupted". !IsInterrupted won't evaluate to the boolean inverse of IsInterrupted; it won't evaluate to anything. It'll get you this in the debug output stream:
System.Windows.Data Error: 40 : BindingExpression path error: '!IsInterrupted' property not found on 'object' 'ThreadDecorator' blah blah blah
<StackPanel
IsEnabled="{Binding !IsInterrupted, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
One way to do this is to write a boolean-inverse value converter (stolen verbatim from Chris Nicol's answer at the other end of that link):
[ValueConversion(typeof(bool), typeof(bool))]
public class InverseBooleanConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (targetType != typeof(bool))
throw new InvalidOperationException("The target must be a boolean");
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
#endregion
}
Usage:
<UserControl.Resources>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter" />
</UserControl.Resources>
<!-- stuff etc. -->
IsEnabled="{Binding Path=IsReadOnly, Converter={StaticResource InverseBooleanConverter}}"
You could also write a Style with a DataTrigger that sets IsEnabled to False if IsInterrupted is true.
I feel like this should be easy, but I am stumped.
I am trying to bind a ComboBox.IsEnabled property to something like "Is an item currently selected on the DataGrid" property using MVVM. This way if no item is selected on the DataGrid, the ComboBox will be disabled.
Is there a DataGrid property that registers True/False when an item is selected, or do I need to do something with the SelectedItems.Count property?
I am trying to do this with as little code as possible before I write a converter or custom property.
//xaml
<DataGrid SelectedItem="{Binding SelectedModelItem}"/>
<ComboBox IsEnabled={Binding IsItemSelected } />
//VM (You will need to implement INotifyPropertyChanged in your ViewModel)
public bool IsItemSelected { get {return null != SelectedModelItem; }
public YourModelType SelectedModelItem{
get{
return selectedModelItem;
}
set{
selectedModelItem = value;
OnPropertyChanged();
}
}
I believe there is no inbuilt property which will say there is one item selected in DataGrid. Instead you can Bind a property to SelectedItem of your DataGrid and Check for SelectedItem is null.
for Example:
<DataGrid
ItemsSource="{Binding ListOfitems}"
SelectedItem="{Binding CurrentItem, Mode=TwoWay}"/>
Then your VM
private object _CurrentItem;
public object CurrentItem
{
get
{
return _CurrentItem;
}
set
{
_CurrentDocument = value;
NotifyPropertyChanged();
//Make your logic for your combobox binding.
}
}
I ended up using a converter to solve the above question. Thank you everybody for your suggestions. I just wanted to make sure I wasn't missing a property, before I implemented this.
XAML
<Control.Resources>
<local:ItemToBoolConverter x:Key="ItemToBoolConverter"/>
</Control.Resources>
<ComboBox IsEnabled="{Binding ElementName=dataGrid, Path=SelectedItems.Count, Converter={StaticResource ItemToBoolConverter}}">
Code Behind
public class ItemToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
try
{
int? itemCount = value as int?;
if (itemCount < 1)
{
return false;
}
else
{
return true;
}
}
catch { return DependencyProperty.UnsetValue; }
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
view
<Window x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cvt="clr-namespace:TestTelerikColumnFooter"
Width="300" Height="300"
>
<Window.Resources>
<cvt:SelectionConverter x:Key="SelectionConverter" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" ItemsSource="{Binding Coll1}" IsEnabled="{Binding SelectedPerson, Converter={StaticResource SelectionConverter}}" DisplayMemberPath="FirstName" Margin="6"/>
<DataGrid Grid.Row="1" IsReadOnly="True" ItemsSource="{Binding Coll2}" SelectedItem="{Binding SelectedPerson}" Margin="6"/>
</Grid>
MainViewmodel:
public class MainViewModel : INotifyPropertyChanged
{
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public float Val { get; set; }
}
private object _selectedPerson;
public object SelectedPerson
{
get { return _selectedPerson; }
set
{
_selectedPerson = value;
OnPropertyChanged("SelectedPerson");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private bool _isItemSelected;
public bool IsItemSelected
{
get { return _isItemSelected; }
set
{
if (value == _isItemSelected)
return;
_isItemSelected = value;
OnPropertyChanged("IsItemSelected");
}
}
private ObservableCollection<Person> _coll1;
public ObservableCollection<Person> Coll1
{
get
{
return _coll1 ?? (_coll1 = new ObservableCollection<Person>());
}
}
private ObservableCollection<Person> _coll2;
public ObservableCollection<Person> Coll2
{
get
{
return _coll2 ?? (_coll2 = new ObservableCollection<Person>());
}
}
public MainViewModel()
{
Coll1.Add(
new Person
{
FirstName = "TOUMI",
LastName = "Redhouane",
Val = 12.2f
});
Coll1.Add(
new Person
{
FirstName = "CHERCHALI",
LastName = "Karim",
Val = 15.3f
});
Coll2.Add(
new Person
{
FirstName = "TOUMI",
LastName = "Djamel",
Val = 12.2f
});
Coll2.Add(
new Person
{
FirstName = "CHERCHALI",
LastName = "Redha",
Val = 12.2f
});
}
}
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
converter :
public class SelectionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I have a ListView with an ComboBox in its ItemTemplate. The ComboBox is also bound to the same list as of the ListView with a Converter. The ComboBox is populated properly but the SelectedItem doesn't show up.
I have tried overriding the Equals method of the underlying object too.
XAML:
<ListView x:Name="FactorsListView"
ItemsSource="{Binding FactorList}" SelectedItem="{Binding SelectedFactor, Mode=OneWayToSource}"
ScrollViewer.CanContentScroll="False">
<ListView.ItemTemplate>
<DataTemplate>
<Grid d:DesignWidth="461.333" d:DesignHeight="368.96">
<StackPanel>
<Grid Height="30.96" Visibility="{Binding IsChecked, ElementName=Monetary, Converter={StaticResource BoolToVis}}">
<Label Content="Related Quantitative Factor:" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Top"/>
<ComboBox Margin="171.707,4,10,0" VerticalAlignment="Top" Width="Auto" ItemsSource="{Binding DataContext.FactorList, ElementName=UcGrid, Converter={StaticResource QtyFacConverter}}" SelectedItem="{Binding RelatedQuantityFactor}" DisplayMemberPath="Name"/>
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
<ListView>
Converter:
public class FactorConverters : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
ObservableCollection<Factor> givenList = value as ObservableCollection<Factor>;
ObservableCollection<Factor> rList = new ObservableCollection<Factor>();
if (givenList != null)
{
foreach(Factor factor in givenList)
{
if (!factor.IsMonetary)
{
rList.Add(factor);
}
}
}
return rList;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Factor Class:
public class Factor : ModelBase
{
private Factor _RelatedQuantityFactor;
public Factor RelatedQuantityFactor
{
get
{
return _RelatedQuantityFactor;
}
set
{
if (_RelatedQuantityFactor != value)
{
_RelatedQuantityFactor = value;
RaisePropertyChanged("RelatedQuantityFactor");
}
}
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Factor))
{
return false;
}
else
{
bool res = ((Factor)obj).ID == this.ID;
return res;
}
}
}
FactorsViewModel class:
public class FactorsViewModel : ViewModelBase
{
private ObservableCollection<Factor> _FactorList;
private RevenueItem _SelectedRevenueItem;
private ObservableCollection<Factor> _UniversalFactors;
private Factor _SelectedFactor;
private ObservableCollection<Factor> _QuantitativeFactorHelperList;
public ObservableCollection<Factor> FactorList
{
get
{
return _FactorList;
}
set
{
if (_FactorList != value)
{
_FactorList = value;
AttachFactorListner(value);
}
}
}
private void AttachFactorListner(ObservableCollection<Factor> value)
{
foreach (Factor factor in value)
{
factor.PropertyChanged += factor_PropertyChanged;
}
RaisePropertyChanged("FactorList");
}
void factor_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsMonetary")
{
RaisePropertyChanged("FactorList");
}
}
public RevenueItem SelectedRevenueItem
{
get
{
return _SelectedRevenueItem;
}
set
{
if (_SelectedRevenueItem != value)
{
_SelectedRevenueItem = value;
RaisePropertyChanged("SelectedRevenueItem");
}
}
}
public ObservableCollection<Factor> UniversalFactors
{
get
{
return _UniversalFactors;
}
set
{
if (_UniversalFactors != value)
{
_UniversalFactors = value;
RaisePropertyChanged("UniversalFactors");
}
}
}
public Factor SelectedFactor
{
get
{
return _SelectedFactor;
}
set
{
if (_SelectedFactor != value)
{
_SelectedFactor = value;
RaisePropertyChanged("SelectedFactor");
}
}
}
public ObservableCollection<Factor> QuantitativeFactorHelperList
{
get
{
return _QuantitativeFactorHelperList;
}
set
{
if (_QuantitativeFactorHelperList != value)
{
_QuantitativeFactorHelperList = value;
RaisePropertyChanged("QuantitativeFactorHelperList");
}
}
}
public FactorsViewModel(RevenueItem revenueItem)
{
_SelectedRevenueItem = revenueItem;
_FactorList = revenueItem.Factors;
AttachFactorListner(_FactorList);
}
}
The View and Viewmodels: PostImg Link
You should bind SelectedItem to Some Property in the ViewModel
As I saw in your code you bound it to the Property RelatedQuantityFactor from your model Factor not from ViewModel
Let's see MVVM pattern.
ViewModel should inherit from InotifyPropertyChanged so that it can update View.
Model is Object which include Property and Behavior,but it doesn't communicate with View
So,if you wang to update your UI ,you should use Data Binding.
I think Factor is ViewModel.
public class Factor: ModelBase
{
private ItemViewModel _selectedFactor;
Public ItemViewModel SelectedFactor
{
get
{
return _selectedFactor;
}
set
{
_selectedFactor = value;
RaisePropertyChanged("RelatedQuantityFactor");
}
}
}
you also should change the Binding Mode.
SelectedItem="{Binding SelectedFactor, Mode=OneWay}"
Finally, don't forget to assign DataContext
FactorsListView.DataContext = new Factor();
Just some question:
Do the viewmodel contain any variable name "RelatedQuantityFactor"?
Try to post your code behind which include d initialization of data context and viewmodel if you still face any problem .
Update :
You can try to put breakpoint into your setter and getter of RelatedQuantityFactor to investigate whether it update your relatedquantityfactor as you expected
Update 2:
See this Difference between SelectedItem, SelectedValue and SelectedValuePath.
Hope its helps :)
I try to connect two checkboxes to my ViewModel. Their behavior is like a radiobutton (exclusive) and TheeState. So both not checked or one of them checked
At the moment I am doing the job like that:
<dxlc:LayoutGroup>
<dxlc:LayoutItem Label="with errors">
<CheckBox IsChecked="{Binding OnlyMusicWithErrorsChecked}"></CheckBox>
</dxlc:LayoutItem>
<dxlc:LayoutItem Label="without errors">
<CheckBox IsChecked="{Binding OnlyMusicWithoutErrorsChecked}"></CheckBox>
</dxlc:LayoutItem>
</dxlc:LayoutGroup>
und ViewModel:
private bool _onlyMusicWithErrorsChecked;
public bool OnlyMusicWithErrorsChecked
{
get { return _onlyMusicWithErrorsChecked; }
set
{
SetProperty(ref _onlyMusicWithErrorsChecked, value, () => OnlyMusicWithErrorsChecked);
if (OnlyMusicWithErrorsChecked)
OnlyMusicWithoutErrorsChecked = false;
RaisePropertyChanged("AdditionalCriteriaHeader");
if (!_filteringData)
SelectData();
}
}
private bool _onlyMusicWithoutErrorsChecked;
public bool OnlyMusicWithoutErrorsChecked
{
get { return _onlyMusicWithoutErrorsChecked; }
set
{
SetProperty(ref _onlyMusicWithoutErrorsChecked, value, () => OnlyMusicWithoutErrorsChecked);
if (OnlyMusicWithoutErrorsChecked)
OnlyMusicWithErrorsChecked = false;
RaisePropertyChanged("AdditionalCriteriaHeader");
if (!_filteringData)
SelectData();
}
}
The question is: can I use only one property nullable bool to do this job?
You can bind both CheckBoxes to the same property OnlyMusicWithErrorsChecked, and in the second CheckBox add a converter that inverts the property's value:
<CheckBox IsChecked="{Binding OnlyMusicWithErrorsChecked, Converter={StaticResource Inverter}}"></CheckBox>
This converter would look somewhat like:
public class Inverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool)
return !((bool)value);
else // Fallback
return false;
}
}
EDIT: If you want to build a three-state-solution with only one bindable property, you'll need two converters (or one that can be parameterized):
public class MyConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty InvertProperty = DependencyProperty.Register(
"Invert", typeof (bool), typeof (MyConverter), new PropertyMetadata(default(bool)));
public bool Invert
{
get { return (bool) GetValue(InvertProperty); }
set { SetValue(InvertProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = (bool?) value;
switch (val)
{
case true:
return Invert;
break;
case false:
return !Invert;
break;
case null:
return false; // None of the checkboxes shall be active
break;
}
// Fallback
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = (bool)value;
switch (val)
{
case true:
return Invert;
break;
case false:
return null;
break;
}
// Fallback
return false;
}
}
The Invert property is set to false for the first checkbox, and true for the second one:
<Window.Resources>
<local:MyConverter x:Key="Converter" Invert="False"/>
<local:MyConverter x:Key="Inverter" Invert="True"/>
</Window.Resources>
Now you can use these two converter instances to bind the checkboxes to the same property:
<CheckBox IsChecked="{Binding MyProperty, Converter={StaticResource Converter}}" />
<CheckBox IsChecked="{Binding MyProperty, Converter={StaticResource Inverter}}" />
If the first box is checked the property will be false, if the second one is checked it will be true, and if no checkbox is checked it will be null.
However, I agree with ANewGuyInTown that you'd be better off with an Enum, since the bool types are a bit confusing here (by the way, most of the converter can be re-used when working with a three-state enum instead of nullable boolean).
Make a "NotConverter" on one of the checkboxes. Here's my implementation I've been using for a while in Windows Store and Phone apps. WPF is similar.
/// <summary>
/// Converts a bool to it's oppisite and back.
/// </summary>
public sealed class NotConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (!(value is bool)) || !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return (value is bool) && (bool)value;
}
}
Register your converter in your App.xaml (or your view) file:
<Application.Resources>
<ResourceDictionary>
<converters:NotConverter x:Key="NotConverter"/>
</ResourceDictionary>
</Application.Resources>
Bind it in your view:
<CheckBox IsChecked="{Binding OnlyMusicWithErrorsChecked,Converter={StaticResource NotConverter}}"></CheckBox>
You could also name your other checkbox and bind to it's property like this:
<CheckBox x:Name="MyCheckBox" IsChecked="{Binding OnlyMusicWithErrorsChecked}"/>
<CheckBox IsChecked="{Binding ElementName=MyCheckBox,Path=IsChecked,Converter={StaticResource NotConverter}}"/>
Three state checkbox.
public bool CheckBox1
{
get { return _checkBox1; }
set
{
_checkBox1 = value;
if (value == true)
{
CheckBox2 = false;
}
OnPropertyChanged("CheckBox1");
}
}
private bool _checkBox2 = false;
public bool CheckBox2
{
get { return _checkBox2; }
set
{
_checkBox2 = value;
if (value == true)
{
CheckBox1 = false;
}
OnPropertyChanged("CheckBox2");
}
}
In Xaml Code something like this
<CheckBox Content="CheckBox1" IsChecked="{Binding CheckBox1, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Height="30" Width="100" />
<CheckBox Content="CheckBox2" IsChecked="{Binding CheckBox2, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Height="30" Width="100" />