Event binding to ViewModel events in XAML - wpf

WPF newbie here. Suppose my ViewModel class has an event SomethingHappened defined as:
Public Event SomethingHappened(message As String)
and there is a local resource in my View define as:
<local:MsgWindow x:Key="MsgWindow" Visibility="Hidden" >
The ViewModel is assigned as the DataContext of the View. How do I change the Visibility of my MsgWindow to Visible upon SomethingHappended?

I would define a bool property in your view model, create BoolToVisibilityConverter (implementing IValueConverter), and bind Visibility property in XAML to your model's property with the converter. Instead of raising the event, set your property (ensure your property raises PropertyChanged from INotifyPropertyChanged OR is a dependency property).

In the resources, declare:
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
Then in your view-model declare:
public bool Visible { get; set; }
And then, bind your component to this property with the converter
<local:MsgWindow x:Key="MsgWindow" Visibility="{Binding Visible, Converter={StaticResource BooleanToVisibilityConverter}}" >

Related

Cannot bind DataGrid's ItemsSource inside WPF User Control

I am designing a UserControl that contains a datagrid. The datagrid is not displaying any rows despite my best efforts to bind it to the ItemsSource.
Here is the basic flow of the binding:
A window that contains the UserControl.
The window viewmodel contains an instance of the viewmodel designed for the user control.
I bind the UserControl's ViewModel to this viewmodel via dependency property.
The datagrid is then binded to the observable collection inside the view model, but nothing is showing.
Code (Xaml and VB.Net):
The window view model, binded to all window controls:
Public Class WindowVM
...
Public Property UserControlViewModel as New UserControlVM
End Class
Window Xaml:
<local:MyUserControl ViewModel="{Binding UserControlViewModel, Mode=OneWay}"/>
User Control code:
Public Shared ReadOnly ViewModelProperty As DependencyProperty = DependencyProperty.Register("ViewModel", GetType(UserControlVM), GetType(MyUserControl), New PropertyMetadata(Nothing))
...
Public Property ViewModel As UserControlVM
Get
Return CType(Me.GetValue(ViewModelProperty), UserControlVM)
End Get
Set(value As UserControlVM)
Me.SetValue(ViewModelProperty, value)
End Set
End Property
....
Public Class UserControlVM
Public Property RunItems As New ObservableCollection(Of RunVM)
End Class
User control xaml, datagrid binding:
<DataGrid DataContext="{Binding ViewModel}"
ItemsSource="{Binding RunItems}" ...
It seems a lot of steps, but to my knowledge this is the correct MVVM way to snake the binding to the DataGrid. So far, nothing.
Looks like I might have solved it. The DataGrid's DataContext needed to be pointed at the UserControl.
Just a simple oversight:
<DataGrid DataContext="{Binding ViewModel, RelativeSource={RelativeSource AncestorType=UserControl}}"
ItemsSource="{Binding RunItems}"

Checkbox binding not working

I searched the forum and did everything as advised to create dependancy property and bind it to checkbox, but for some reason it doesn't bind.
<CheckBox IsChecked="{Binding ElementName=MainWindow, Path=isLoop}" Content="" Height="22" HorizontalAlignment="Left" Margin="250,208,0,0" x:Name="checkBox1" VerticalAlignment="Top" Width="22" />
C#
public bool isLoop
{
get { return (bool)GetValue(isLoopProperty); }
set { SetValue(isLoopProperty, value); }
}
public static readonly DependencyProperty isLoopProperty =
DependencyProperty.Register("isLoop", typeof(bool), typeof(MainWindow), new UIPropertyMetadata(true));
You've made some key mistakes in your sample.
First, you are not binding to an object that supports your "isLoop" property (unless "MainWindow" is a custom control that has that property). Somewhere in that CheckBox's hierarchy, you need to set the DataContext to an object that supports it, or bind to an element that has that property.
Second, you should rarely, if ever, create a dependency property in your business object. For business objects, follow the INotifyPropertyChanged pattern. Typically, you should create dependency properties in visual UI elements, such as custom controls in order to be able to bind data to them (a target, not the source).
So, to fix your problem, you should probably create an object that implements INotifyPropertyChanged, create an IsLoop property that throws the NotifyPropertyChanged event in the setter, and set this object as the DataContext to the CheckBox's parent container (or further up the hierarchy if appropriate).
HTH
You are binding to the Window itself. Do you mean to do that? Unless your code example is in the code behind then the binding will not work.
Since you're using an ElementName binding, I am guessing you are binding to a UI element. The problem is, none of the default UI elements come with a property called isLoop, so your binding is invalid.
There are a few things you can try.
If your isLoop property is part of the object named MainWindow's DataContext, change your binding to DataContext.isLoop
<CheckBox IsChecked="{Binding ElementName=MainWindow, Path=DataContext.isLoop}" ... />
If isLoop is actually a property on a custom class called MainWindow, such as your dependency property implies, verify that the object named MainWindow is actually of type MainWindow
<local:MainWindow x:Name="MainWindow" />
And if neither of those work, post your full XAML (particularly the part named MainWindow), the code for the class MainWindow, and the code that ties the MainWindow class object with the XAML UI.
The isLoop won't trigger when the checkbox is clicked. That is simply for accessing the depency property in code. You should add a PropertyCallback function and register that in the metadata.

How to bind local variable in WPF

I have silverlight usercontrol. This contains Service Entity object. see below
public partial class MainPage : UserControl
{
public ServiceRef.tPage CurrentPage { get; set; }
...
}
I need to bind CurrentPage.Title to TextBox
My xaml is here
<TextBox Text="{Binding Path=CurrentPage.Title, RelativeSource={RelativeSource self}}"></TextBox>
But it is not work.
How to do it?
In order for that to work, you'll have to implement INotifyPropertyChanged on your class and raise the PropertyChanged event for CurrentPage when it's set (this also means you won't be able to use auto properties; you'll have to use your own private instance backing variable and code the get { } and set { } yourself).
What's happening is the control is binding to the value before you've set CurrentPage. Because you aren't notifying anyone that the property has changed, it does not know to refresh the bound data. Implementing INotifyPropertyChanged will fix this.
Or you could just manually set the Text property yourself in the setter.
Change your markup to
<TextBlock Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=CurrentPage.Title}" />
By assigning RelativeSource={RelativeSource self} your are telling the TextBlock to bind to itself and look for a property named CurrentPage on the TextBlock itself and not the parent Window.
set the UpdateSourceTrigger="PropertyChanged" in the XAML.

Binding from View-Model to View-Model of a child User Control in Silverlight? 2 sources - 1 target

So i have a UserControl for one of my Views and have another 'child' UserControl inside that.
The outer 'parent' UserControl has a Collection on its View-Model and a Grid control on it to display a list of Items.
I want to place another UserControl inside this UserControl to display a form representing the details of one Item.
The parent UserControl's View-Model already has a property on it to hold the currently selected Item and i would like to bind this to a DependancyProperty on the child UserControl. I would then like to bind that DependancyProperty to a property on the child UserControl's View-Model.
I can then set the DependancyProperty once in XAML with a binding expression and have the child UserControl do all its work in its View-Model like it should.
The code i have looks like this..
Parent UserControl:
<UserControl x:Class="ItemsListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source={StaticResource ServiceLocator}, Path=ItemsListViewModel}">
<!-- Grid Control here... -->
<ItemDetailsView Item="{Binding Source={StaticResource ServiceLocator}, Path=ItemsListViewModel.SelectedItem}" />
</UserControl>
Child UserControl:
<UserControl x:Class="ItemDetailsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Source={StaticResource ServiceLocator}, Path=ItemDetailsViewModel}"
ItemDetailsView.Item="{Binding Source={StaticResource ServiceLocator}, Path=ItemDetailsViewModel.Item, Mode=TwoWay}">
<!-- Form controls here... -->
</UserControl>
EDIT: Heres how i created the Dependancy Proeprty on the child UC:
public partial class ItemDetailsView : UserControl
{
private static readonly DependencyProperty itemProperty;
static ItemDetailsView()
{
ItemDetailsView.itemProperty = DependencyProperty
.Register("Item", typeof(Item), typeof(ItemDetailsView), null);
}
public Item Item
{
get { return (Item)GetValue(ItemDetailsView.itemProperty); }
set { SetValue(ItemDetailsView.itemProperty, value); }
}
public static Item GetItem(DependencyObject target)
{
return (Item)target.GetValue(itemProperty);
}
public static void SetItem(DependencyObject target, Item value)
{
target.SetValue(itemProperty, value);
}
}
The selected Item is bound to the DependancyProperty fine. However from the DependancyProperty to the child View-Model does not.
It appears to be a situation where there are two concurrent bindings which need to work but with the same target for two sources.
Why won't the second (in the child UserControl) binding work?? Is there a way to acheive the behaviour I'm after??
Cheers.
Well, it looks like you are trying to use a "normal" DependencyProperty on the parent UserControl, and an "attached" DependencyProperty on the child UserControl. You need to pick one way. :)
EDIT for clarification:
There are two ways of registering a dependency property, "Normal", like so:
public static readonly DependencyProperty BobProperty =
DependencyProperty.Register("Bob",....)
and Attached:
public static readonly DependencyProperty BobAttachedProperty =
DependencyProperty.RegisterAttached("BobAttached",...)
Let's say the control you are registering these properties on is called "MyPanel". To use each property:
<MyPanel Bob="somevalue" MyPanel.BobAttached="somevalue"/>
Note the need to specify "where the attached property is defined". Attached properties are great when you have some bit of behavior or functionality that applies to multiple types of controls.
That said, perhaps there is a better way to do this - If the parent UserControl contained an ItemsControl, the ItemTemplate for that control could be a DataTemplate that contained the ItemDetailsView, in which case you could use standard data binding to do what you needed to:
<UserControl blahblahblah>
<ItemsControl ItemsSource="{Binding WhereYourItemsAre}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ns:WhatYourChildViewIsCalled DataContext="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UserControl>

How to change a WPF control's visibility from ViewModel

I've an WPF application where tried to implement MVVM pattern and Prism 2. I have a Usercontrol which has subscribed to an event fired from another Usercontrol. I would like to toggle visibility of few child elements in the subscribing control. Events are fired properly, even I am successfully able to bind data to some elements. How do I bind Visibility or any style property for that matter with the ViewModel and change them dynamically.
You can have a boolean property in your ViewModel and bind that property to the Visibility property of your controls. Since you will be asigning a boolean value and the Visibility property is expecting a Visibility enumeration value, you will have to use the BooleanToVisibilityConverter converter to make the conversion,
<Style.Resources>
<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
</Style.Resources>
<Image Visibility="{Binding Path=ShowImage,
Converter={StaticResource booleanToVisibilityConverter}}"/>
Hope this helps.
Ezequiel Jadib
Although adding a Boolean property and using a value converter works, I would recommend adding a property of type Visibility to your ViewModel, e.g.
public Visibility ImageVisibility
{
get { return shouldShowImage ? Visibility.Visible : Visibility.Collapsed }
}
The advantage of this method is you don't need to write a converter for every property you want to express in a visual way (e.g. for a stock level that turns a label red when it drops below 10, you could have a converter you use once or just expose a StockLabelBrush property from your VM)
There's a simple solution for people who run into this issue.
In your view model, create a "Visibility" property like so:
public Visibility ShowModifyButtons
{
get { return (Visibility)GetValue(ShowModifyButtonsProperty); }
set { SetValue(ShowModifyButtonsProperty, value); }
}
public static readonly DependencyProperty ShowModifyButtonsProperty =
DependencyProperty.Register("ShowModifyButtons", typeof(Visibility), typeof(FileMatchViewModel),
new UIPropertyMetadata(Visibility.Collapsed));
In your XAML, bind to it like so:
<Button Focusable="False" Content="Save" Width="100" Margin="10" Visibility="{Binding ShowModifyButtons}"/>
Now, from your view model, you can set ShowModifyButtons to Visibility.Collapsed or Visibility.Visible as needed.

Resources