WPF databinding ToolTips won't get updated - wpf

I need to display some ToolTips and have a CheckBox to display it and hide it. I have the databinding but it won't get updated. The bool value seems always true which means that the ToolTip won't disappear even when the CheckBox is unchecked.
WPF Code
<Button Name="btnCancel" Content="Cancel"
ToolTipService.IsEnabled="{Binding GeneralDisplayTooltipsForPreferencesAndSearchOptions}"
ToolTip="Discard Changes On All Tabs and Close Dialog"
Height="25" Width="80" Margin="0,5,2,5"
VerticalAlignment="Center" Click="btnCancel_Click"/>
C#
public class General : INotifyPropertyChanged, IPreferencesGeneral
{
private bool generalDisplayTooltipsForPreferencesAndSearchOptions = false;
[field: NonSerializedAttribute()]
public event PropertyChangedEventHandler PropertyChanged = delegate
{
};
public bool GeneralDisplayTooltipsForPreferencesAndSearchOptions
{
get { return generalDisplayTooltipsForPreferencesAndSearchOptions; }
set
{
if (!Equals(generalDisplayTooltipsForPreferencesAndSearchOptions, value))
{
generalDisplayTooltipsForPreferencesAndSearchOptions = value;
OnPropertyChanged("GeneralDisplayTooltipsForPreferencesAndSearchOptions");
}
}
}
}

If it's not for the all application you should let the view handle it.
Do something like that:
<CheckBox x:Name="checkBoxTooltip" ></CheckBox>
<Button ToolTip="Cancel" ToolTipService.IsEnabled="{Binding ElementName=checkBoxTooltip, Path=IsChecked}" />

Check if you have correct DataContext. Also see output if you have any binding errors.
Also, if you want to control ToolTip enable state from CheckBox (and the check box is in the same context) you may just bind IsEnabled to CheckBox IsChecked (with relative binding)
This code works on my machine, see example:
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350">
<Grid>
<CheckBox VerticalAlignment="Top"
IsChecked="{Binding GeneralDisplayTooltipsForPreferencesAndSearchOptions}"
Content="Check me!" />
<Button Name="btnCancel"
Width="80"
Height="25"
Margin="0,5,2,5"
VerticalAlignment="Center"
Content="Cancel"
ToolTip="Discard Changes On All Tabs and Close Dialog"
ToolTipService.IsEnabled="{Binding GeneralDisplayTooltipsForPreferencesAndSearchOptions}" />
</Grid>
And the viewmodel:
namespace WpfApplication11
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new General();
}
}
public class General : INotifyPropertyChanged
{
private bool generalDisplayTooltipsForPreferencesAndSearchOptions = false;
[field: NonSerializedAttribute()]
public event PropertyChangedEventHandler PropertyChanged = delegate
{
};
public bool GeneralDisplayTooltipsForPreferencesAndSearchOptions
{
get { return generalDisplayTooltipsForPreferencesAndSearchOptions; }
set
{
if (!Equals(generalDisplayTooltipsForPreferencesAndSearchOptions, value))
{
generalDisplayTooltipsForPreferencesAndSearchOptions = value;
OnPropertyChanged("GeneralDisplayTooltipsForPreferencesAndSearchOptions");
}
}
}
private void OnPropertyChanged(string p)
{
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
}
}

Related

WPF Frame and Page Get event

Is that possible in wpf, in main window will capture the event of the page inside the frame element?
<Window>
<Grid>
<TextBlock x:Name="lblEvent"/>
<Frame Source="Page1.xaml"/>
</Grid>
</Window>
<Page>
<Grid>
<Button Content="Click Me"/>
</Grid>
</Page>
If the button has been clicked, the textblock in the main window will update the text to "Page1 Button click".
If you use MVVM patter this would be very easy:
Define your ViewModel class:
class MyViewModel:INotifyPropertyChanged
{
private string _LabelText;
public string LabelText
{
get
{
return this._LabelText;
}
set
{
if (value != this._LabelText)
{
this._LabelText = value;
NotifyPropertyChanged();
}
}
}
private DelegateCommand _ClickCommand;
public readonly DelegateCommand ClickCommand
{
get
{
if(_ClickCommand == null)
{
_ClickCommand = new DelegateCommand(()=>LabelText="LabelText Changed!");
}
return _ClickCommand;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then in your Window set the DataContext:
public class MainWindow
{
private MyViewModel vm;
public MainWindow()
{
InitializeComponent();
this.vm = new MyViewModel()
DataContext = vm;
}
}
Set the binding inside the View Code:
<Window>
<Grid>
<TextBlock x:Name="lblEvent" Text="{Binding LabelText}"/>
<Frame Source="Page1.xaml"/>
</Grid>
</Window>
<Page>
<Grid>
<Button Content="Click Me" Command="{Binding ClickCommand}"/>
</Grid>
</Page>
As you can see there is any event delegate, but just a command that handle the click of the button. You can find more informations here: Mvvm Basics; Commands; Prism Command

Bind a multiple properties of a button in WPF to a class

I'm not sure if this is possible but I'm looking for a way to bind a button to a generic class that contain all the properties i will need to use. Every button needs a relay command so that would be included but all of our buttons will need to bind visibility and being enabled. Instead of having this group of properties and relay command for every button we will use within the given windows view model I was wondering if there was a way to have the button bind to a class then in our view model we reference a new instance of that class for each button needed and then be just be able to set the properties on that class to the values we need. I hope this makes sense.
There's probably a bunch of different ways to do something like this. I don't know if I'd choose to have a class instance for each button. But here's a rough/quick/dodgy example of a solution.
The main model for the form is providing the button models by way of a list. The individual button models then handle the button bindings.
EDIT: Extended the code a bit. Now includes command bindings. Also shows use of ItemsControl as suggested by #Xavier. Hope it helps.
MainWindow.xaml:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<StackPanel>
<!-- Known buttons -->
<StackPanel Margin="20">
<Button DataContext="{Binding ButtonModels[0], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
<Button DataContext="{Binding ButtonModels[1], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
<Button DataContext="{Binding ButtonModels[2], Mode=OneTime}" Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
</StackPanel>
<!-- Dynamic buttons -->
<StackPanel Margin="20">
<ItemsControl ItemsSource="{Binding ButtonModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding LabelText}" Background="{Binding Colour}" Command="{Binding Command}" CommandParameter="{Binding CommandParameter}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</StackPanel>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Model();
}
}
public class Model
{
private Random rnd = new Random();
public List<ButtonModel> ButtonModels { get; private set; }
public Model()
{
this.ButtonModels = new List<ButtonModel>();
for (int i = 0; i < 5; i++)
{
this.ButtonModels.Add(new ButtonModel
{
LabelText = "Button " + (i + 1),
Command = new RelayCommand((index) => { this.ChangeColour((int)index); }),
CommandParameter = i
});
}
}
private void ChangeColour(int index)
{
this.ButtonModels[index].Colour = new SolidColorBrush(Color.FromRgb((byte)rnd.Next(50, 256), (byte)rnd.Next(50, 256), (byte)rnd.Next(50, 256)));
}
}
public class ButtonModel : ObservableObject
{
private string _LabelText;
public string LabelText { get => _LabelText; set => this.SetProperty(ref _LabelText, value); }
private Brush _Colour = new SolidColorBrush(Color.FromRgb(205, 205, 205));
public Brush Colour { get => _Colour; set => this.SetProperty(ref _Colour, value); }
private RelayCommand _Command;
public RelayCommand Command { get => _Command; set => this.SetProperty(ref _Command, value); }
private int _CommandParameter;
public int CommandParameter { get => _CommandParameter; set => this.SetProperty(ref _CommandParameter, value); }
}
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (field == null && value == null)
{
return false;
}
if (field == null || !field.Equals(value))
{
field = value;
this.RaisePropertyChangedEvent(propertyName);
return true;
}
return false;
}
protected void RaisePropertyChangedEvent(string propertyName)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class RelayCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> action, Predicate<object> canExecute = null)
{
this.execute = action;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
}

WPF ListView not updating at runtime

I have a listview's itemsource binded to a Observable collection of Animal class.
When the window loads up, listview displays all the items correctly.
But I have a button which deletes an item from the observablecollection which did not update the listview.
Expected Behaviour: Button click should delete first item in observable collection and update the UI
Observed Behaviour: Button click should deletes first item in observable collection but did not update the UI
public class Animal
{
public int Num { get; set; }
public string Name { get; set; }
}
public class ViewModel:INotifyPropertyChanged
{
private ObservableCollection<Animal> animals;
public ObservableCollection<Animal> Animals
{
get { return animals; }
set { animals = value; OnPropertyChanged("Animals"); }
}
public ViewModel()
{
Animals = new ObservableCollection<Animal>()
{
new Animal(){ Name="ASD", Num = 1},
new Animal(){ Name="XYZ", Num = 2},
};
}
public void Run()
{
Animals.RemoveAt(0);
}
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<Grid DataContext="{Binding Source={StaticResource ViewModelDataSource}}">
<Button Content="Button" HorizontalAlignment="Left" Height="20" Margin="70,285,0,0" VerticalAlignment="Top" Width="100" Click="Button_Click"/>
<ListView x:Name="mylistview" HorizontalAlignment="Left" Height="212" Margin="42,47,0,0" VerticalAlignment="Top" Width="446" ItemsSource="{Binding Animals}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Num}"/>
<Label Content="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
public partial class MainWindow : Window
{
private ViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
vm.Run();
}
}
ListView uses DataContext="{Binding Source={StaticResource ViewModelDataSource}}.
In a Window you create another instance of a view model (vm = new ViewModel();). After that you have 2 different instances and collections. vm.Run(); removes item from collection which is not connected to view.
You need to work with one instance, so try to find the same resource, which is used in the view:
public MainWindow()
{
InitializeComponent();
vm = (ViewModel)this.FindResource("ViewModelDataSource");
}
Also DataContext setter can be simplified:
`DataContext="{StaticResource ViewModelDataSource}"`
it is preferable to follow MVVM aproach and get rid of code behind:
1] declare command property in a viewmodel
public ICommand RunCmd { get; private set; }
2] use some ready-made ICommand implementation, e.g. RelayCommand or DelegateCommand and initialize RunCmd property from viewmodel constructor:
RunCmd = new RelayCommand(Run);
3] bind Button to that command:
<Button Content="Button"
HorizontalAlignment="Left"
Height="20" Width="100" Margin="70,285,0,0"
VerticalAlignment="Top"
Command="{Binding RunCmd}"/>
note, that Click handler is removed

Checkbox twoway mode not updating viewmodel

Very simple issue here. I have some checkboxes with their IsChecked bindings set to properties in my viewmodel.The binding mode is twoway. However, when they are checked, the viewmodel property isnt updated. I found a post about setting the clickmode of the checkbox and I have tried all the options:Hover, Press and Release. None of these fix the issue.
Is your property a nullable bool like the CheckBox.IsChecked?
Otherwise verify all that is needed for the MVVM pattern to work: your property is public with a getter and a setter, implementing INotifyPropertyChanged, etc.
Are the other properties binding properly? Your DataContext may be wrong...
Try this:
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" >
<StackPanel>
<CheckBox Width="250" Height="30" IsChecked="{Binding Path=IsTrue, Mode=TwoWay}" />
<TextBlock Text="{Binding Path=IsTrue}" />
</StackPanel>
</Window>
Create ViewModel:
public class MainWindowViewModel :INotifyPropertyChanged
{
private bool _isTrue;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChange(string propertyName)
{
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public bool IsTrue
{
get { return _isTrue; }
set
{
_isTrue = value;
OnPropertyChange("IsTrue");
}
}
}
Bind to View Model in MainWindow.cs code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}

enabling disabling button on checkbox check/uncheck wpf mvvm

I have list of checkboxes on a window specifying some items to be ordered. I need to first disable the Order button when the windows loads and enable it after selecting/check some items(checkboxes) and vice versa. I have bind the IsChecked property of the checkbox.
Edit Import from OP comment:-
I have only one checkbox in the ItemsControl. and I have bind the ItemsControl's ItemsSource to List. that way we can show multiple checkboxes as per the items in the List.
Here is the code:
<ItemsControl ItemsSource="{Binding FavoriteItems}" Margin="80,0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel>
<Grid>
<CheckBox IsChecked="{Binding IsHouseholdSelected}" Content="{Binding SubCategoryName}" Grid.ColumnSpan="1" FontFamily="Calibri" FontWeight="Bold" />
</Grid>
</StackPanel>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
Below is a sample code that could help you out. Basically, the key here is I had the Items in the list implicitly notify its parent ViewModel's Command object to raise the CanExecuteChanged event every time the IsChecked property changes. (Also, I'm using "DelegateCommand" here, which is just the same as "RelayCommand").
ViewModels:
public class ViewModel : INotifyPropertyChanged
{
public DelegateCommand MyCommand { get; set; }
private ObservableCollection<ItemViewModel> items = new ObservableCollection<ItemViewModel>();
public ObservableCollection<Item> Items
{
get { return this.items; }
}
public ViewModel()
{
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 1" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 2" });
this.items.Add(new ItemViewModel(this) { IsChecked = false, Text = "Item 3" });
this.MyCommand = new DelegateCommand(this.CanExecute, this.Execute);
}
public void Execute(object parameter)
{
MessageBox.Show("Executed");
}
public bool CanExecute(object parameter)
{
return (this.items.Count == this.items.Count((x) => x.IsChecked));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
#endregion
}
public class ItemViewModel
{
private ViewModel parent;
private bool isChecked;
public string Text { get; set; }
public bool IsChecked
{
get { return this.isChecked; }
set
{
this.isChecked = value;
if (this.parent.MyCommand != null)
this.parent.MyCommand.OnCanExecuteChanged(null);
}
}
public Item(ViewModel parent)
{
this.parent = parent;
}
}
View:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<DockPanel>
<Button DockPanel.Dock="Bottom" Command="{Binding MyCommand}">Test</Button>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" Content="{Binding Text}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DockPanel>
</Window>
Bind a command to the button and implement the CanExecute method to check the status of the checkboxes and either enable or disable the button and use the Execute method to invoke the functionality that you want on the button.
MVVM RelayCommand
CanExecute on MSDN
EDIT: Here is some source code of how to implement a RelayCommand. The RelayCommand class can be found at the first link provided above. I'm assuming that you know how to hook up the DataContext to the ViewModel implementation.
<StackPanel>
<CheckBox Name="MyCheckBox" Content="Some CheckBox"
IsChecked="{Binding MyCheckBoxChecked}"/>
<Button Content="Click me" Command="{Binding MyCommand}"/>
</StackPanel>
public class OrderViewModel
{
private RelayCommand MyRelayCommand;
public OrderViewModel()
{
MyRelayCommand = new RelayCommand(Execute, CanExecute);
MyCheckBoxChecked = false;
}
public RelayCommand MyCommand
{
get { return MyRelayCommand; }
}
public bool MyCheckBoxChecked { get; set; }
private bool CanExecute(object o)
{
// Here I'm just checking the property we've bound to but you can put
// anything in here that will return a bool, including a check of any/all
// of the checkboxes you may need to check
return MyCheckBoxChecked;
}
private void Execute(object o)
{
Console.WriteLine(#"Executing ...");
}
}

Resources