I am using this code following code taken from Stackoverflow.
I want to transfer a string from one view model to another one on SelectionChanged event. But when I click on Tab2, I get Tab2 message box, but when I click on Tab1, I get both the message boxes indicating that both are getting executed. The same when I click Tab1, both message boxes are seen.
MainView.xaml
<TabControl>
<TabItem Header="My tab 1" Selector.IsSelected="{Binding IsMyTab1Selected}"> ... </TabItem>
<TabItem Header="My tab 2" Selector.IsSelected="{Binding IsMyTab2Selected}"> ... </TabItem>
</TabControl>
MainViewModel.cs
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainViewModel() {
PropertyChanged += handlePropertyChanged;
}
public bool IsMyTab1Selected {
get { return _IsMyTab1Selected ; }
set {
if (value != _IsMyTab1Selected ) {
_IsMyTab1Selected = value;
OnPropertyChanged("IsMyTab1Selected ");
}
}
}
private bool _IsMyTab1Selected = false;
public bool IsMyTab2Selected {
get { return _IsMyTab2Selected ; }
set {
if (value != _IsMyTab2Selected ) {
_IsMyTab2Selected = value;
OnPropertyChanged("IsMyTab2Selected ");
}
}
}
private bool _IsMyTab2Selected = false;
private void handlePropertyChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "IsMyTab1Selected") {
MessageBox.Show("Tab_1 Clicked!");
} else if (e.PropertyName == "IsMyTab2Selected") {
MessageBox.Show("Tab_2 Clicked!");
}
}
I am not able to get the mutually exclusiveness, point me where I am wrong.
Option 1
you can change the setters to only call OnPropertyChanged(..) when the value is true:
public bool IsMyTab1Selected
{
get { return _IsMyTab1Selected; }
set
{
if (value != _IsMyTab1Selected)
{
_IsMyTab1Selected = value;
if (_IsMyTab1Selected)
OnPropertyChanged("IsMyTab1Selected");
}
}
}
public bool IsMyTab2Selected
{
get { return _IsMyTab2Selected; }
set
{
if (value != _IsMyTab2Selected)
{
_IsMyTab2Selected = value;
if(_IsMyTab2Selected)
OnPropertyChanged("IsMyTab2Selected");
}
}
}
Option 2
Or you can check in your handlePropertyChange() if the value is true like this
private void handlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "IsMyTab1Selected")
{
if(IsMyTab1Selected)
MessageBox.Show("Tab_1 Clicked!");
}
else if (e.PropertyName == "IsMyTab2Selected")
{
if(IsMyTab2Selected)
MessageBox.Show("Tab_2 Clicked!");
}
}
The bindings will update on deselection too. You need to check e.NewValue in your handler, or why not simply check _IsMyTab1Selected etc?
Related
Full solution: https://github.com/fallingsappy/portfolio/tree/master/DDrop
I have three collections. First one:
public class Series : INotifyPropertyChanged
{
private ObservableCollection<DropPhoto> _dropPhotosSeries;
public ObservableCollection<DropPhoto> DropPhotosSeries
{
get
{
return _dropPhotosSeries;
}
set
{
_dropPhotosSeries = value;
OnPropertyChanged(new PropertyChangedEventArgs("DropPhotosSeries"));
}
}
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
return _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1 && _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters == null).ToList().Count == 0;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
Second:
public class DropPhoto : INotifyPropertyChanged
{
private Drop _drop;
public Drop Drop
{
get
{
return _drop;
}
set
{
_drop = value;
OnPropertyChanged(new PropertyChangedEventArgs("Drop"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
Last:
public class Drop : INotifyPropertyChanged
{
private double? _radiusInMeters;
public double? RadiusInMeters
{
get
{
return _radiusInMeters;
}
set
{
_radiusInMeters = value;
OnPropertyChanged(new PropertyChangedEventArgs("RadiusInMeters"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
I want to update (invoke?) property CanDrawPlot every time something is happens to two other collection (Drop and DropPhot.cs). For example, if DropPhotosSeries.Count goes lower then 2 I need to change CanDrawPlot to false. CanDrawPlot should update UI. Here is the XAML:
<TabItem IsEnabled="{Binding CurrentSeries.CanDrawPlot, ElementName=AppMainWindow,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" x:Name="SingleSeriesPlotTabItem" Header="График" >
<uc:ScatterPlot x:Name="SingleSeriesPlot" User="{Binding User, ElementName=AppMainWindow}" ParticularSeriesIndex="{Binding ParticularSeriesIndex, ElementName=AppMainWindow}"/>
</TabItem>
CurrentSeries is instantiated in MainWindowXaml.cs:
public static readonly DependencyProperty CurrentSeriesProperty = DependencyProperty.Register("CurrentSeries", typeof(Series), typeof(MainWindow));
public Series CurrentSeries
{
get { return (Series)GetValue(CurrentSeriesProperty); }
set
{
SetValue(CurrentSeriesProperty, value);
}
}
---------------UPDATE---------------
I changed my code accordingly to Rob's answer:
public class Series : INotifyPropertyChanged
{
public Series()
{
_dropPhotosSeries = new ObservableCollection<DropPhoto>();
_dropPhotosSeries.CollectionChanged += _dropPhotosSeries_CollectionChanged;
}
private void _dropPhotosSeries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanDrawPlot)));
}
private bool _canDrawPlot;
public bool CanDrawPlot
{
get
{
return _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters != null).ToList().Count > 1 && _dropPhotosSeries?.Where(x => x.Drop.RadiusInMeters == null).ToList().Count == 0;
}
set
{
_canDrawPlot = value;
OnPropertyChanged(new PropertyChangedEventArgs("CanDrawPlot"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
}
Now CanDrawPlot correctly notifies changes in DropPhoto Collection. But I need to invoke it also by changes in inner Drop class of DropPhotoSeries. Here what i did:
public class Drop : INotifyPropertyChanged
{
Series _series;
public Drop(Series series)
{
_series = series;
}
private double? _radiusInMeters;
public double? RadiusInMeters
{
get
{
return _radiusInMeters;
}
set
{
_radiusInMeters = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(_series)));
OnPropertyChanged(new PropertyChangedEventArgs("RadiusInMeters"));
}
}
}
its not working, what's wrong?
You need to hook up to CollectionChanged event of the ObservableCollection and in there you have to raise PropertyChanged for CanDrawPlot.
EXAMPLE:
Add constructor to the Series class and in the constructor instantiate the observable collection and subscribe to the CollectionChanged event.
public Series()
{
_dropPhotosSeries = new ObservableCollection<DropPhoto>();
_dropPhotosSeries.CollectionChanged += _dropPhotosSeries_CollectionChanged;
}
private void _dropPhotosSeries_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanDrawPlot)));
}
I'm new to WPF and I'm having some trouble with my existing setup to get the list box selected item to appear in the text box.
The picture here represents the issue. I typed "12 HOUR" in the text box, which then filters the listbox to those items with "12 HOUR" anywhere in the string. But when I click "12 Hour Nasal" in the list box, I now want to reflect that choice back in the text box:
http://i.imgur.com/ZCYAolT.png
Here is my XAML for the user control containing the listbox and textbox:
<UserControl x:Class="SCM_AllergyRecModule.SearchAndSelectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<StackPanel Width="300">
<TextBox x:Name="Filter" Text="{Binding Path=Filter, UpdateSourceTrigger=PropertyChanged}"/>
<ListBox Width ="300" Height="50" x:Name="ListBoxControl"
ItemsSource="{Binding Path=Allergens}"
SelectedItem="{Binding Path=SelectedAllergen}">
</ListBox>
</StackPanel>
And here is the ViewModel:
namespace SCM_AllergyRecModule
{
public class SearchAndSelectViewModel
{
private ICollectionView allergens;
private string selectedAllergen;
private string filter = "";
public string Filter
{
get
{
return this.filter.ToUpperInvariant();
}
set
{
if (this.filter != value)
{
this.filter = value;
this.Allergens.Refresh();
}
}
}
private bool ContainsFilter(object item)
{
var product = item as string;
if (product == null)
{
return false;
}
if (string.IsNullOrEmpty(this.Filter))
{
return true;
}
if (product.ToUpperInvariant().Contains(this.Filter))
{
return true;
}
return false;
}
public SearchAndSelectViewModel()
{
var cvs = new CollectionViewSource();
cvs.Source = MainWindow.scmAllergens;
this.allergens = cvs.View;
this.allergens.Filter = ContainsFilter;
}
public ICollectionView Allergens
{
get
{
return this.allergens;
}
}
public string SelectedAllergen
{
get
{
return this.selectedAllergen;
}
set
{
if (this.selectedAllergen != value)
{
this.selectedAllergen = value;
}
}
}
}
}
Update 1
I added the INotifyPropertyChanged interface to my class and have it being raised on SelectedAllergen in the setter. I added an event handler called SearchAndSelectViewModel_PropertyChanged to handle the SelectedAllergen property changing and set it in the constructor.
Now when I click an item in the listbox, I do see it setting the Filter to the SelectedItem and the list filters to that item so nothing else shows. But still, the text box text is not changing? See screenshot below. This is after I typed in "PEAN" in the textbox, then the listbox filtered to two choices, and I chose "PEANUTS (FOOD)", which then refiltered the list box to just show that choice but didn't set the text box to "PEANUTS (FOOD)":
http://imgur.com/dNxuVI5
Updated ViewModel
public class SearchAndSelectViewModel : INotifyPropertyChanged
{
private ICollectionView allergens;
private string selectedAllergen;
private string filter;
public string Filter
{
get
{
return this.filter.ToUpperInvariant();
}
set
{
if (this.filter != value)
{
this.filter = value;
this.Allergens.Refresh();
}
}
}
private bool ContainsFilter(object item)
{
var product = item as string;
if (product == null)
{
return false;
}
if (string.IsNullOrEmpty(this.Filter))
{
return true;
}
if (product.ToUpperInvariant().Contains(this.Filter))
{
return true;
}
return false;
}
private void SearchAndSelectViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "SelectedAllergen":
this.Filter = this.SelectedAllergen;
break;
}
}
public SearchAndSelectViewModel()
{
filter = "";
var cvs = new CollectionViewSource();
cvs.Source = MainWindow.scmAllergens;
this.allergens = cvs.View;
this.allergens.Filter = ContainsFilter;
this.PropertyChanged += SearchAndSelectViewModel_PropertyChanged;
}
public ICollectionView Allergens
{
get
{
return this.allergens;
}
}
public string SelectedAllergen
{
get
{
return this.selectedAllergen;
}
set
{
if (this.selectedAllergen != value && value != null)
{
this.selectedAllergen = value;
OnPropertyChanged("SelectedAllergen");
}
}
}
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You need to implement the INotifyPropertyChanged interface and you can raise it in your property setter. Since you are also binding your TextBox to the Filter property you need to set the Filter property when your SelectedAllergen changes.
INotifyPropertyChanged example:
public class MyViewModel : INotifyPropertyChanged
{
//...
private int myProperty = 0;
public int MyProperty
{
get { return myProperty; }
set
{
myProperty = value;
// Raise the property changed notification
OnPropertyChanged("MyProperty");
}
}
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if(handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
there is probably a really simple reason why this isnt working but I've tried everything. I have a TextBlock with Text bound to a variable, the variable changes but the Text doesn't :
<TextBlock x:Name="modeLabel" Style="{StaticResource IndiTextBlock}" Height="23" TextWrapping="Wrap" Grid.Row="0" Text="{Binding ModeLabelText}" Margin="35,22,58,0"/>
The code that controls the text value is in a viewmodel:
public string ModeLabelText { get { return _modeLabeltext; } }
public ComboBoxItem SelectedMode { get { return _selectedMode; }
set
{
if (_selectedMode == value) return;
_selectedMode = value;
ToggleMode(null);
EvaluateScenario(null);
}
and
private void ToggleMode(object parameter)
{
if (_isBasicCalculation)
{
_modeLabeltext = "Target profit";
_isBasicCalculation = false;
}
else
{
_modeLabeltext = "Total to invest";
_isBasicCalculation = true;
}
}
Your class has to implement the INotifyPropertyChanged interface, and on changes of your variables, you should trigger the event
public class Model : INotifyPropertyChanged
{
public event EventHandler PropertyChanged; // event from INotifyPropertyChanged
protected void RaisePropertyChanged(string propertyName)
{
var local = PropertyChanged;
if (local != null)
{
local.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public void ToggleMode()
{
// ... your code ...
RaisePropertyChanged("ModelLabelText");
}
}
Thank you Nguyen Kien
private void ToggleMode(object parameter)
{
if (_isBasicCalculation)
{
_modeLabeltext = "Target profit";
OnPropertyChanged("ModeLabelText");
_isBasicCalculation = false;
}
else
{
_modeLabeltext = "Total to invest";
OnPropertyChanged("ModeLabelText");
_isBasicCalculation = true;
}
}
I'm currently working with sliders in WPF. My GUI window has 2 sliders that are supposed to act together in a few ways. slider1 must always be less than or equal to slider2, and slider2 must always be greater than or equal to slider1. My first attempt at using C# code-behind to solve this problem is documented in my previous question. This question got my code to compile, but did not effect any visual change in my program during run time. What would be the ideal method to making these sliders run in the way that I need them to?
Thank you.
Lets say that your ViewModel have 2 properties Slider1 and Slider2 and your XAML looks something like this:
<Slider Value="{Binding Path=Slider1}"/>
<Slider Value="{Binding Path=Slider2}"/>
then you can do your logic in ViewModel when Slider1 or Slider2 is changed:
public class MyClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private double _slider1;
public double Slider1
{
get { return _slider1; }
set
{
if (_slider1 != value)
{
_slider1 = value;
OnPropertyChanged("Slider1");
if (_slider1 > Slider2) Slider2 = _slider1;
}
}
}
private double _slider2;
public double Slider2
{
get { return _slider2; }
set
{
if (_slider2 != value)
{
_slider2 = value;
OnPropertyChanged("Slider2");
if (_slider2 < Slider1) Slider1 = _slider2;
}
}
}
}
for your ease you ca do this also..
private void slider1_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (slider1 == null || slider2 == null)
return;
if (slider1.Value >= slider2.Value)
{
slider2.Value = slider1.Value;
}
}
private void slider2_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (slider1 == null || slider2 == null)
return;
if (slider2.Value <= slider1.Value)
{
slider1.Value = slider2.Value;
}
}
Hello can any one please help me in my code,
i have xaml:
<ListView Name="__Listview" ItemsSource="{Binding Path=DisplayItems}">
<CheckBox Content="{Binding Items}" IsChecked="{Binding Path=IsChecked, Mode=TwoWay, NotifyOnTargetUpdated=True}" Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"/>
</ListView>
<CheckBox Name="_checkBoxSelectAll" Checked="CheckBoxToCheckAll" Unchecked="CheckBoxToCheckAll"/>
Code C#:
public partial class DisplayItems
{
private ObservableCollection<Records> _displayItems = new ObservableCollection< Records>();
public DisplayItems ()
{
InitializeComponent();
_ Listview.DataContext = this;
}
public ObservableCollection< Records > DisplayItems
{
get { return _displayItems; }
set { _displayItems = value; }
}
private void CheckBoxToCheckAll(object sender, RoutedEventArgs e)
{
if (_checking || !(sender is CheckBox))
return;
CheckBox checkBox = (CheckBox)sender;
_checking = true;
foreach (Records swElement in DisplayItems)
{
bool val;
if (checkBox.IsChecked != null)
val = checkBox.IsChecked.Value;
else
val = false;
swElement.IsChecked = val;
}
_checking = false;
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
if ((sender as CheckBox) == null || ((sender as CheckBox).DataContext as Records) == null || _checking)
return;
_checking = true;
if (_checkBoxSelectAll.IsChecked != null && _listTo.All(select => select.IsChecked) && !_checkBoxSelectAll.IsChecked.Value)
_checkBoxSelectAll.IsChecked = true;
else if (_checkBoxSelectAll.IsChecked == null || _checkBoxSelectAll.IsChecked.Value)
_checkBoxSelectAll.IsChecked = false;
_checking = false;
}
}
Public class Records
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isChecked = false;
Public Records(){}
public bool IsChecked
{
get { return _isChecked; }
set { _isChecked = value; OnPropertyChanged("IsChecked"); }
}
protected void OnPropertyChanged(string name)
{
if ((PropertyChanged != null) && (_notification))
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public enum Items{One,Two,three,}
}
if i Check _checkBoxSelectAll , All checkBoxs in Listview should check, my problem is code behind its IsChecked=true, but in UI not visible that Checkbox is checked, pleae help me in advance
Implement INotifyPropertyChanged on Records?
Public class Records**: INotifyPropertyChanged**
Replace your records class with below please
Public class Records : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isChecked = false;
Public Records(){}
public `bool IsChecked`
{
get { return _isChecked; }
set { _isChecked = value; OnPropertyChanged("IsChecked"); }
}
protected void OnPropertyChanged(string name)
{
if ((PropertyChanged != null) && (_notification))
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public enum Items{One,Two,three,}
}