Hi I am following this tutorial,http://blogs.u2u.be/diederik/post/2011/11/14/null.aspx, to bind the visibility of an element to a Boolean property. The program is not working. Here is the code:
<Page.Resources>
<local:BooleanToVisibilityConverter x:Key="TrueToVisibleConverter"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<TextBlock Text=" Hello World"
Visibility="{Binding Path=Show_element, Converter={StaticResource TrueToVisibleConverter}}"/>
<Button Click="Button_Click">press button</Button>
</StackPanel>
</Grid>
public sealed partial class MainPage : Page , INotifyPropertyChanged
{
private bool show_element ;
public bool Show_element
{
get { return show_element; }
set
{
show_element = value;
this.OnPropertyChanged();
Debug.WriteLine("Show_element value changed");
}
}
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Show_element = !Show_element;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged(string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class BooleanToVisibilityConverter : IValueConverter
{
public bool IsReversed { get; set; }
public object Convert(object value, Type typeName, object parameter, string language)
{
var val = System.Convert.ToBoolean(value);
if (this.IsReversed)
{
val = !val;
}
if (val)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
The visibility does not change with the property. I was having an error due to intellisense (Error Xaml namespace) which was resolved. Not sure what is wrong with this code.
Thank you.
change
this.OnPropertyChanged();
to
this.OnPropertyChanged("Show_element");
edit:
besides that, you don't have a ViewModel (sorry, missed that when I was checking your code), so you need to create one and set it as DataContext:
ViewModel.cs:
public class ViewModel : INotifyPropertyChanged
{
private bool show_element;
public bool Show_element
{
get { return show_element; }
set
{
show_element = value;
this.OnPropertyChanged("Show_element");
Debug.WriteLine("Show_element value changed");
}
}
public ViewModel()
{
}
public void ButtonClicked()
{
Show_element = !Show_element;
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged(string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
and your MainPage.xaml.cs should look somehow like that:
public sealed partial class MainPage : Page
{
private ViewModel _viewModel;
public MainPage()
{
this.InitializeComponent();
_viewModel = new ViewModel();
DataContext = _viewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_viewModel.ButtonClicked();
}
}
Related
I have to bind Grid Drop Event and PreviewMouseLeftButtonDown event in ViewModel. I have a RelayCommand. But it is done only for passing the object, I have to pass the routed event by using the command and also for MouseButtonEventArgs. my sample code is as below, please give any suggestion for using the routed event args and MouseButtonEventArgs in viewmodel.
<Grid
x:Name="mainGrid"
AllowDrop="True"
Background="#F0F0F0">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<cmd:EventCommandExecuter Command="{Binding GridDrop}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
<Grid Background="LightBlue" PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown">
EventCommandExecuter
public class EventCommandExecuter : TriggerAction<DependencyObject>
{
#region Constructors
public EventCommandExecuter()
: this(CultureInfo.CurrentCulture)
{
}
public EventCommandExecuter(CultureInfo culture)
{
Culture = culture;
}
#endregion
#region Properties
#region Command
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
#region EventArgsConverterParameter
public object EventArgsConverterParameter
{
get { return (object)GetValue(EventArgsConverterParameterProperty); }
set { SetValue(EventArgsConverterParameterProperty, value); }
}
public static readonly DependencyProperty EventArgsConverterParameterProperty =
DependencyProperty.Register("EventArgsConverterParameter", typeof(object), typeof(EventCommandExecuter), new PropertyMetadata(null));
#endregion
public IValueConverter EventArgsConverter { get; set; }
public CultureInfo Culture { get; set; }
#endregion
protected override void Invoke(object parameter)
{
var cmd = Command;
if (cmd != null)
{
var param = parameter;
if (EventArgsConverter != null)
{
param = EventArgsConverter.Convert(parameter, typeof(object), EventArgsConverterParameter, CultureInfo.InvariantCulture);
}
if (cmd.CanExecute(param))
{
cmd.Execute(param);
}
}
}
}
I want to pass object and RoutedEventArgs like below in viewmodel. Please help
public void Grid_Drop(object sender, RoutedEventArgs e)
{
}
I feel like commands are often overkill for such simple tasks.
You can simply declare your ViewModel in the code behind of your view like so:
public partial class MainWindow : Window
{
private ViewModel _vm;
public ViewModel Vm
{
get { return _vm;}
set
{
_vm = value ;
}
}
//....Constructor here....
}
Then create a public event :
public event RoutedEventHandler OnGridDrop;
and call it in :
public void Grid_Drop(object sender, RoutedEventArgs e)
{
OnGridDrop?.Invoke(sender,e)
}
Now you only need to initialize your ViewModel:
public MainWindow()
{
InitializeComponent();
Vm = new ViewModel();
OnGridDrop += Vm.OnGridDrop;
}
and subscribe a corrsponding handler that you declared in your ViewModel.
I have the following textbox
<TextBox Grid.Column="1"
Grid.Row="1"
Name="groupAddressBox"
Width ="80"
Text="{Binding Path=GroupAddress, Converter={StaticResource groupAddressConverter}}"/>
When I change the text manually, it's all good.
But when I try to do this via a button
private void Test_Click(object sender, RoutedEventArgs e)
{
groupAddressBox.Text = "0/0/1";
}
Although the text changes, the source is not updated, and when I click on ok, it recognizes the value that was there before the change.
I cannot upgrade the source straight away, so I prefer to do this this way.
Is there something that can help me force the source upgrade via this way?
Based on your question, I tried to create a Simple Example of MVVM Pattern with very basic functionality. Please do necessary change to XAML and CS file as I took the highlighted code only.
Helper Classes
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class CommandHandler : ICommand
{
public event EventHandler CanExecuteChanged { add { } remove { } }
private Action<object> action;
private bool canExecute;
public CommandHandler(Action<object> action, bool canExecute)
{
this.action = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute;
}
public void Execute(object parameter)
{
action(parameter);
}
}
ViewModel
public class ViewModel : ViewModelBase
{
private string groupAddress;
public string GroupAddress
{
get
{
return groupAddress;
}
set
{
if(value != groupAddress)
{
groupAddress = value;
OnPropertyChanged("GroupAddress");
}
}
}
public ViewModel()
{
}
private ICommand clickCommand;
public ICommand ClickCommand
{
get
{
return clickCommand ?? (clickCommand = new CommandHandler(() => MyAction(), true));
}
}
public void MyAction()
{
GroupAddress = "New Group Address";
}
}
Window Xaml
<TextBox Grid.Column="1" Grid.Row="1" Width ="80"
Text="{Binding GroupAddress, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Push" Style="{StaticResource TransparentButtonStyle}"
Margin="5" Command="{Binding ClickCommand}"/>
Window Xaml cs
ViewModel vm = new ViewModel();
this.DataContext = vm;
I would learn to use converter on Wpf(xaml).
<Window x:Class="TextExpanderGriglia.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TextExpanderGriglia"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</Window.Resources>
<Grid>
<Button Content="CIAO" Width="50" Height="50" Visibility="{Binding vButton,Converter={StaticResource BoolToVisibilityConverter}}"> </Button>
<Button Content="Cambia" Width="50" Height="50" Margin="56,134,411,135" Click="Button_Click"/>
</Grid>
This is my Xaml code.
Stupid example to start, I have 2 button and on button "Cambia" I set boolean value vButton = !vButton, but also if vButton is false the first button doesn't hide.
What is missing in my code?
This is my converter
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (Visibility)value == Visibility.Visible;
}
}
MainWindows.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool vButton;
public event PropertyChangedEventHandler PropertyChanged;
public bool VButton
{
get
{
return vButton;
}
set
{
if (value != vButton)
{
this.vButton = value;
NotifyPropertyChanged("VButton");
}
}
}
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
vButton = !vButton;
}
}
The converter looks fine.
Most likely scenario here is that the property you are binding to is not raising change notifications. For example:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isButtonVisible;
public bool vButton
{
get { return _isButtonVisible; }
set
{
if (value == _isButtonVisible)
return;
_isButtonVisible = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
To set DataContext
In your constructor:
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
In MVVM pattern, you should define the ViewModel separately from your Windows.
For ex. in your case:
public class MainWindowViewModel : INotifyPropertyChanged
{
private bool vButton;
public event PropertyChangedEventHandler PropertyChanged;
public bool VButton
{
get
{
return vButton;
}
set
{
if (value != vButton)
{
this.vButton = value;
NotifyPropertyChanged("VButton");
}
}
}
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then in your MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
this my xml code
<TextBlock Grid.Column="0" Tag="{Binding id,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" Text="{Binding Name,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
this is my model
public string _Name;
public string Name
{
get { return _Name; }
set { _Name = value; RaisePropertyChanged("Name"); }
}
when i set value to these two propertie ie. to id and Name
but its not notifying to Name ...
Simple Databinding Example with Updates. You can use this as a reference to get you started :)
public partial class MainWindow : Window, INotifyPropertyChanged
{
// implement the INotify
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _mytext;
public String MyText
{
get { return _mytext; }
set { _mytext = value; NotifyPropertyChanged("MyText"); }
}
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this; // set the datacontext to itself :)
MyText = "Change Me";
}
}
<TextBlock Text="{Binding MyText}" Foreground="White" Background="Black"></TextBlock>
I need Model to notify ViewModel if any property is changed, because I need to collect the changed model instances in a collection for further processing, also to enable and disable command buttons in the viewmodel.
So I've used ModelBase abstract class and added HasChanges property which I can test against in the viewmodel and catch the changed models.But it is not working and I don't know what i'm missing.
public abstract class ModelBase : INotifyPropertyChanged
{
protected ModelBase()
{
}
private bool _hasChanges;
public bool HasChanges
{
get
{
return _hasChanges;
}
set
{
if (_hasChanges != value)
{
_hasChanges = value;
RaisePropertyChanged("HasChanges");
}
}
}
protected void RaisePropertyChanged(string propertyName)
{
HasChanges = true;
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
The Model is wrapped inside the ViewModel and bound to the View which is a DataGrid:
private Model_selectedModel;
public Mode SelectedModel
{
get
{
return _selectedModel;
}
set
{
if (_selectedModel != value)
{
_selectedModel = value;
NotifyPropertyChanged("SelectedModel");
}
}
}
Thanks for valuable help.
I tested your class and it's okay. I think the point is a typo here:
private Model_selectedModel;
public Mode SelectedModel
{
get
{
return _selectedModel;
}
set
{
if (_selectedModel != value)
{
_selectedModel = value;
NotifyPropertyChanged("SelectedModel");
}
}
}
There should be RaisePropertyChanged instead of NotifyPropertyChanged.
Below it's my test:
XAML
<Window x:Class="TestUpdatePropertyChanged.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:TestUpdatePropertyChanged"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<this:TestViewModel />
</Window.DataContext>
<Grid>
<TextBox Width="100" Height="25" Text="{Binding Path=TestString, UpdateSourceTrigger=PropertyChanged}" />
<Button Width="100" Height="30" VerticalAlignment="Top" Content="Click" Click="Button_Click" />
</Grid>
</Window>
Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var testData = this.DataContext as TestViewModel;
testData.TestString = "Yay, it's change!";
if (testData.HasChanges == true)
{
MessageBox.Show("It's Change!");
}
}
}
public class TestViewModel : ModelBase
{
private string _testString = "test";
public string TestString
{
get { return _testString; }
set
{
if (_testString != value)
{
_testString = value;
RaisePropertyChanged("TestString");
}
}
}
}
public abstract class ModelBase : INotifyPropertyChanged
{
protected ModelBase()
{
}
private bool _hasChanges;
public bool HasChanges
{
get
{
return _hasChanges;
}
set
{
if (_hasChanges != value)
{
_hasChanges = value;
RaisePropertyChanged("HasChanges");
}
}
}
protected void RaisePropertyChanged(string propertyName)
{
HasChanges = true;
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}