Bind DependencyProperty of Usercontrol in ListBox - c#

I need ListBox with my UserControl listed in it. My UserControl has TextBox. So I want to display property of List's subitem in UserControl's textBox. I have tried a lot of options with DataContext and ElementName - it just doesn`t work. I just stucked on it. The only way to make it work is to remove DataContext binding of UserControl to itself and change Item Property name so it matches to DependencyProperty name - but I need to reuse my control in different viewmodels with different entities so it is almost not possible to use the approach.
Interesting thing is that if I change my UserControl to Textbox and bind Text property of it - everything works. What the difference between Textbox and my UserControl?
So let me just show my code.
I have simplified the code to show only essential:
Control XAML:
<UserControl x:Class="TestControl.MyControl"
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"
d:DesignHeight="100" d:DesignWidth="200"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBlock Text="{Binding Text}"/>
</Grid>
</UserControl>
Control CS:
public partial class MyControl : UserControl
{
public MyControl()
{
InitializeComponent();
}
public string Text
{
get {
return (string)this.GetValue(TextProperty); }
set {
this.SetValue(TextProperty, value); }
}
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyControl), new propertyMetadata(""));
}
Window XAML:
<Window x:Class="TestControl.MainWindow"
Name="_windows"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestControl"
Title="MainWindow" Height="350" Width="525" >
<Grid Name="RootGrid">
<ListBox ItemsSource="{Binding ElementName=_windows, Path=MyList}">
<ItemsControl.ItemTemplate >
<DataTemplate >
<local:MyControl Text="{Binding Path=Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Grid>
</Window>
Window CS:
public partial class MainWindow : Window
{
public MainWindow()
{
_list = new ObservableCollection<Item>();
_list.Add(new Item("Sam"));
_list.Add(new Item("App"));
_list.Add(new Item("H**"));
InitializeComponent();
}
private ObservableCollection<Item> _list;
public ObservableCollection<Item> MyList
{
get { return _list;}
set {}
}
}
public class Item
{
public Item(string name)
{
_name = name;
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}

This is a pretty big gotcha in XAML. The problem is that when you do this in the user control:
DataContext="{Binding RelativeSource={RelativeSource Self}}"
You change its data context, so that in this line:
<local:MyControl Text="{Binding Path=Name}"/>
The runtime will now attempt to resolve "Name" on the instance of "MyControl", instead of on the inherited data context (ie, the view model). (Confirm this by checking the Output window -- you should see a binding error to that effect.)
You can get around this by, instead of setting the user control's data context that way, using a RelativeSource binding:
<UserControl x:Class="TestControl.MyControl"
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"
d:DesignHeight="100" d:DesignWidth="200"
<Grid>
<TextBlock
Text="{Binding Text,RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</Grid>
</UserControl>

Related

WPF/C# Assigning a ViewModel to a custom control from parent view

I'm very new to C# and WPF, and I'm struggling a bit to get data where I need it.
I have one master set of data, which needs to be shared with various user controls, each of which have their own ViewModel. The problem is that I don't seem to be able to assign a ViewModel to a control from the parent XAML and then access that ViewModel from within the custom control's XAML.
I bind the control to a Viewmodel, but then the datacontext within the control doesn't allow me to access that model within the xaml, or I can set the datacontext in the user control so I can access its viewmodel, but then I can't bind to the viewmodel in xaml (because the binding is looking in the local datacontext, not the parent).
I may be going about this all wrong, most examples I've seen seem to instantiate a ViewModel in the custom control xaml, but then I don't see how you get that ViewModel to reference the correct DataModel (or specific part of the datamodel).
The following hopefully explains what I am trying to do.
Firstly I have my data model, in DataModel.cs
using System;
using System.Collections.Generic;
namespace BindingTest1
{
public class DataModel
{
private List<string>[] _dataLists;
public List<string>[] DataLists
{
get { return _dataLists; }
}
public DataModel()
{
List<string> list0 = new List<string> { "One", "Two", "Three" };
List<string> list1 = new List<string> { "Alpha", "Beta", "Gamma" };
_dataLists = new List<String>[] { list0, list1 };
}
}
}
In MainViewModel.cs
namespace BindingTest1
{
class MainViewModel
{
private MyViewModel _myFirstViewModel;
public MyViewModel MyFirstViewModel
{
get { return _myFirstViewModel; }
}
private MyViewModel _mySecondViewModel;
public MyViewModel MySecondModel
{
get { return _mySecondViewModel; }
}
private DataModel _dataModel;
public DataModel DataModel
{
get { return _dataModel; }
}
public MainViewModel()
{
_dataModel = new DataModel();
_myFirstViewModel = new MyViewModel(_dataModel.DataLists[0]);
_mySecondViewModel = new MyViewModel(_dataModel.DataLists[0]);
}
}
}
MainWindow.xaml
<Window x:Class="BindingTest1.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:BindingTest1"
mc:Ignorable="d"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<StackPanel HorizontalAlignment="Stretch" Height="100" VerticalAlignment="Top" Orientation="Horizontal">
<!-- These were just to check the data was being set up properly -->
<ListBox x:Name="listBox1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" ItemsSource="{Binding DataModel.DataLists[0]}"/>
<ListBox x:Name="listBox2" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" ItemsSource="{Binding DataModel.DataLists[1]}"/>
<!-- this is what I want to be able to do -->
<local:MyView ViewModel="{Binding MyFirstViewModel}"/>
<local:MyView ViewModel="{Binding MySecondViewModel}"/>
</StackPanel>
</Grid>
</Window>
(Codebehind is default)
In MyViewModel.cs
using System;
using System.Collections.Generic;
namespace BindingTest1
{
public class MyViewModel
{
private List<string> _dataList;
public List<string> DataList
{
get { return _dataList; }
}
public MyViewModel(List<string> list)
{
_dataList = new List<String>(list);
_dataList.Add("Some Local Processing");
}
}
}
MyView.xaml
<UserControl x:Class="BindingTest1.MyView"
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"
xmlns:local="clr-namespace:BindingTest1"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="100">
<Grid>
<ListBox x:Name="listBox" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" ItemsSource="{Binding ViewModel.DataList}"/>
</Grid>
</UserControl>
Codebehind
using System.Windows;
using System.Windows.Controls;
namespace BindingTest1
{
/// <summary>
/// Interaction logic for MyView.xaml
/// </summary>
public partial class MyView : UserControl
{
public MyViewModel ViewModel
{
get { return (MyViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(MyViewModel), typeof(MyView),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, new PropertyChangedCallback(OnViewModelChanged)));
public MyView()
{
InitializeComponent();
}
private static void OnViewModelChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// Just making sure the right thing is being received
List<string> dataList = (e.NewValue as MyViewModel).DataList;
foreach(string line in dataList)
{
System.Console.WriteLine(line);
}
}
}
}
I don't think you need a dependency property here.
Try this.
<local:MyView DataContext="{Binding MyFirstViewModel}"/>
<local:MyView DataContext="{Binding MySecondViewModel}"/>
and bind the DataList to ItemsSource in the MyView XAML.
As you assigned MyFirstViewModel to the DataContext of MyView, bindings inside will look in MyFirstViewModel for the ItemsSource.
Here's how you ought to do this. Your view doesn't need a ViewModel property. It should bind to properties of its DataContext, which will be the viewmodel.
view:
ItemsSource="{Binding DataList}"
Window:
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModel}">
<local:MyView
/>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel HorizontalAlignment="Stretch" Height="100" VerticalAlignment="Top" Orientation="Horizontal">
<!-- ... -->
<ContentControl Content="{Binding MyFirstViewModel}"/>
<ContentControl Content="{Binding MySecondViewModel}"/>
</StackPanel>

Losing Binding / Object reference when binding Popup.IsOpen to an ObservableCollection through IValueConverter

currently i'm trying to bind the Popup.IsOpen property to an ObservableCollection through an IValueConverter, determining if the collection has items (true) or not(false).
Problem:
IValueConverter is only fired once at initializing the application and then never again.
View:
<UserControl x:Class="AutoCompleteTextBox.Views.AutoCompleteTextBoxView"
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"
xmlns:local="clr-namespace:AutoCompleteTextBox.Views"
xmlns:converter="clr-namespace:AutoCompleteTextBox.Converter"
xmlns:viewmodels="clr-namespace:AutoCompleteTextBox.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<converter:CollectionHasItemsToBooleanConverter x:Key="collectionHasItemsToBoolean" />
</UserControl.Resources>
<UserControl.DataContext>
<viewmodels:AutoCompleteTextBoxViewModel />
</UserControl.DataContext>
<Grid>
<TextBox
Name="txtSearchBox"
Text="{Binding SearchString,UpdateSourceTrigger=PropertyChanged,Mode=OneWayToSource}" />
<Popup
IsOpen="{Binding UserCollection,
Converter={StaticResource collectionHasItemsToBoolean},UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
Width="{Binding ElementName=txtSearchBox,Path=ActualWidth}"
PlacementTarget="{Binding ElementName=txtSearchBox}">
<ListView
ItemsSource="{Binding UserCollection,UpdateSourceTrigger=PropertyChanged,Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name,UpdateSourceTrigger=PropertyChanged,Mode=OneWay}">
</Label>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Popup>
</Grid>
</UserControl>
ViewModel:
public class AutoCompleteTextBoxViewModel : BindableBase
{
private string searchString;
public string SearchString
{
get { return searchString; }
set
{
if (SetProperty(ref searchString, value))
{
if (UserCollection.Count == 3) UserCollection.Clear();
if (searchString != string.Empty)
{
UserCollection.Add(new UserModel() { Name = DateTime.Now.ToLongTimeString() });
}
}
}
}
public ObservableCollection<UserModel> UserCollection { get; set; }
public AutoCompleteTextBoxViewModel()
{
UserCollection = new ObservableCollection<UserModel>();
}
}
Details:
Class BindableBase implements INotifyPropertChanged, is fired by using SetProperty inside the setter of a property.
While setting PopUp.IsOpen manually to true, the desired popup is working.
Binding PopUp.IsOpen to ObservableCollection.Count works (with customized IValueConverter for checking int values).
Does anyone see the error i am currently missing or some hints?
Have a nice weekend!
Daniel
Comment from CrudaLilium worked:
"I believe the problem is that when you are making changes to UserCollection only CollectionChanged is fired and this one won't trigger update. You would have to fire PropertyChanged with the name of UserCollection from your VM for it to trigger."

Binding DependencyProperty in nested user controls

I've this problem: I've made 2 user controls: NestedControl1 and NestedControl2. NestedControl2 contains NestedControl1, and NestedControl1 contains just a TextBlock.
I've set each NestedControl* DataContext to Self, and created a dependency property for each one.
NestedControl1.MyText1 and NestedControl2.MyText2.
Then I've bound the NestedControl2.MyText1 to MyText2, and the TextBlock.Text to MyText1 .
If I use the NestedControl2 on a Window and set MyText2 to whatever, it does not work. However if I use directly the NestedControl1 on a Window, it does work. The point is that I would like to make the value of MyText2 arrive to the TextBlock.Text property inside of NestedControl1 .
The code is the following.. What's wrong?? Any idea?? Thank tou in advance for the answers
NestedControl2 code:
public partial class NestedControl2 : UserControl
{
public NestedControl2()
{
InitializeComponent();
}
public static readonly DependencyProperty MyText2Property = DependencyProperty.Register(
"MyText2", typeof(string), typeof(NestedControl2), new PropertyMetadata(default(string)));
public string MyText2
{
get { return (string)GetValue(MyText2Property); }
set { SetValue(MyText2Property, value); }
}
}
NestedControl2 xaml:
<UserControl x:Class="TestNestedPropertiesWpf.NestedControl2"
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"
xmlns:testNestedPropertiesWpf="clr-namespace:TestNestedPropertiesWpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid>
<testNestedPropertiesWpf:NestedControl1 MyText1="{Binding MyText2}" />
</Grid>
NestedControl1 code:
public partial class NestedControl1 : UserControl
{
public NestedControl1()
{
InitializeComponent();
}
public static readonly DependencyProperty MyText1Property = DependencyProperty.Register(
"MyText1", typeof(string), typeof(NestedControl1), new PropertyMetadata(default(string)));
public string MyText1
{
get { return (string)GetValue(MyText1Property); }
set { SetValue(MyText1Property, value); }
}
}
NestedControl1 xaml:
<UserControl x:Class="TestNestedPropertiesWpf.NestedControl1"
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"
xmlns:testNestedPropertiesWpf="clr-namespace:TestNestedPropertiesWpf"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBlock Text="{Binding MyText1}"
x:Name="textBlock" Foreground="Red"
Width="300" Height="100" Background="Black"></TextBlock>
</Grid>
And in the end, this is MainWindow.xaml:
<Window x:Class="TestNestedPropertiesWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:testNestedPropertiesWpf="clr-namespace:TestNestedPropertiesWpf"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<testNestedPropertiesWpf:NestedControl1 MyText1="WORKING"/>
<testNestedPropertiesWpf:NestedControl2 MyText2="NOT WORKING"/>
</StackPanel>
This won't work:
testNestedPropertiesWpf:NestedControl1 MyText1="{Binding MyText2}"
add a clr property to deliver the value from one DP to another:
add a .Net property "MyBindableText" to NestedControl2, make the DP MyText1 bind to it.
add an OnPropertyChanged handler in the MyText2 DP's registration. In this handler, assign the new value of the DP to "MyBindableText"
Now If you set MyText2="bla", the value is forwarded to Mytext1, and will be set for both DP's MyText1 and MyText2
I found the solution. The NestedControl2 had DataContext to self, and used NestedControl1, wich had DataContext to self, so the two DataContext were different.
To solve the problem I modified the binding declared in the MyText1 like this:
<testNestedPropertiesWpf:NestedControl1 MyText1="{Binding RelativeSource={RelativeSource AncestorType={x:Type testNestedPropertiesWpf:NestedControl2}}, Path=MyText2}" />

How to Bind to a Custom Controls Button Visibility from Within Another Control

I have a custom control, which has a button:
<UserControl x:Class="Gambit.Views.FileSelectionControl"
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"
SnapsToDevicePixels="True"
mc:Ignorable="d">
...
<Button Content="Load"
Margin="5,5,5,5"
Height="22"
Width="70"
IsDefault="True"
IsEnabled="{Binding SelectedFileExists}"
AttachedCommand:CommandBehavior.Event="Click"
AttachedCommand:CommandBehavior.Command="{Binding CloseDialogCommand}"/>
...
</UserControl>
I want to include this control, in another control, but I want to set the Load buttons visibility at design time in the host control; something like
<UserControl x:Class="Gambit.Views.SomeOtherControl"
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"
SnapsToDevicePixels="True"
mc:Ignorable="d">
...
<GroupBox Header="Select Test Data">
<Views:FileSelectionControl <Here Set the Load Button Visibility>/>
</GroupBox>
...
</UserControl>
where <Here Set the Load Button Visibility> shows where i want to set the visibility of the control. How is this done [without breaking the MVVM pattern]?
Thanks for your time.
You can create DependencyProperty in your UserControl:
public partial class SomeView : UserControl
{
...
public static DependencyProperty ButtonVisibilityProperty = DependencyProperty.Register("ButtonVisibility", typeof(Visibility), typeof(SomeView));
public Visibility ButtonVisibility
{
get { return (Visibility)GetValue(ButtonVisibilityProperty); }
set { SetValue(ButtonVisibilityProperty, value); }
}
}
bind it to Button.Visibility:
<UserControl x:Class="WpfApplication2.SomeView"
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">
<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=ButtonVisibility}" Content="My Button"/>
</UserControl>
and then you can control Visibility from outside like so:
<local:SomeView ButtonVisibility="Collapsed"/>
and because it's a DependencyProperty you can use Binding as well
Hi just create a bool or Visibility Type property in UserControl1 and set it in Usercontrol2 like
UserControl1 xaml
<UserControl x:Class="WpfApplication4.UserControl1"
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"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Button x:Name="Loadbutton" Content="load"/>
</Grid>
xaml.cs
public UserControl1()
{
InitializeComponent();
}
bool showLoadButton;
public bool ShowLoadButton
{
get { return showLoadButton; }
set
{
showLoadButton = value;
if (showLoadButton)
Loadbutton.Visibility = Visibility.Visible;
else
Loadbutton.Visibility = Visibility.Collapsed;
}
}
UserControl2 Set ShowLoadButton True or false
<UserControl x:Class="WpfApplication4.UserControl2"
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"
xmlns:local="clr-namespace:WpfApplication4"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<local:UserControl1 ShowLoadButton="True"/>
</Grid>
If you do not want to define a property in the UserControl, which you can always create attached dependency property, and you can declare it in a separate class under the common namespace.
Something like this:
MainWindow.xaml
<local:TestUserControl AttachedProperties:ButtonExt.Visibility="Visible" />
TestUserControl.xaml
<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
Path=(AttachedProperties:ButtonExt.Visibility)}"
Content="TestButton" />
Attached property definition:
public static class ButtonExt
{
public static readonly DependencyProperty VisibilityProperty;
public static void SetVisibility(DependencyObject DepObject, Visibility value)
{
DepObject.SetValue(VisibilityProperty, value);
}
public static Visibility GetVisibility(DependencyObject DepObject)
{
return (Visibility)DepObject.GetValue(VisibilityProperty);
}
static ButtonExt()
{
PropertyMetadata VisibiltyPropertyMetadata = new PropertyMetadata(Visibility.Collapsed);
VisibilityProperty = DependencyProperty.RegisterAttached("Visibility",
typeof(Visibility),
typeof(ButtonExt),
VisibiltyPropertyMetadata);
}
}
Some notes about code-behind in MVVM
I agree with #dkozl, his example does not violate the principle of MVVM, in some cases, the code is present in the View, for example (personally, I always try to avoid code-behind):
Installation DataContext.
The use of different patterns such as Mediator, Proxy, etc.
Determination of properties and behaviors that pertain only to View (as in your case).
The most important thing when you use the code-behind, it is that all actions possible through ViewModel occurred, ie in ViewModel contains all the logic and for example, in the View click event, call the function, which is in ViewModel.
For more information about code-behind, please see the answers to the recent question:
WPF MVVM Code Behind

Custom usercontrol property binding failure silverlight

I have a custom usercontrol with DataContext="{Binding RelativeSource={RelativeSource self}}"
On the code behind i've made a dependency property like:
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) => { new Base().OnPropertyChanged("ElementName"); })));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
Now when I try to use this usercontrol in my mainpage.xaml and use the following binding: <test.TestControl ElementName="{Binding name}" />, it keeps searching for 'name' property in my custom usercontrol instead of where it should come from?
What am I doing wrong ?
It searches there because you have the DataContext set on the topmost level for your user control. What you would need to do is get rid of the relative binding to self in the user control and specify ElementName in bindings (inside user control). Btw you probably don't need OnPropertyChanged in the PropertyChangedCallback cause DependencyProperties in their nature notify about value changes.
I eventually solved it this way. Not the way I wanted, but it's a (in my eyes) pretty neat solution.
CustomUserControl.xaml
<UserControl x:Class="TestApp.Controls.CustomUserControl"
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"
mc:Ignorable="d"
Width="75"
Height="75">
<Canvas x:Name="LayoutRoot"
Background="Black">
<StackPanel Orientation="Vertical">
<Image x:Name="UCImage"
Width="50"
Height="50"
HorizontalAlignment="Center" />
<TextBlock x:Name="UCText"
HorizontalAlignment="Center" />
</StackPanel>
</Canvas>
</UserControl>
CustomUserControl.xaml.cs
public partial class ElementControl : UserControl
{
#region DependencyProperty ElementNameProperty
public static DependencyProperty ElementNameProperty = DependencyProperty.Register("ElementName",
typeof(string),
typeof(ElementControl),
new PropertyMetadata(new PropertyChangedCallback((s, e) =>
{
//See Here
((ElementControl)s).UCText.Text = e.NewValue as string;
})));
public string ElementName
{
get
{
return (string)base.GetValue(ElementNameProperty);
}
set
{
base.SetValue(ElementNameProperty, value);
}
}
#endregion
}

Categories

Resources