Dependency Property ListBox - c#

I want to use a dependency property so that my label displays values selected in the Listbox. This is just to more clearly understand the working of a dependency property.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPFToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
xmlns:local="clr-namespace:WpfApplication1"
x:Name="MyWindow"
Height="200"
Width="300">
<StackPanel>
<ListBox x:Name="lbColor"
Width="248"
Height="56"
ItemsSource="{Binding TestColor}"/>
<StackPanel>
<Label Content="{Binding Path=Test, ElementName=lbColor}" />
</StackPanel>
</StackPanel>
</Window>
Code Behind,
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public ObservableCollection<string> TestColor { get; set; }
public String Test
{
get { return (String)GetValue(TestProperty); }
set { SetValue(TestProperty, value); }
}
// Using a DependencyProperty as the backing store for Title. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TestProperty =
DependencyProperty.Register("Test", typeof(String), typeof(ListBox), new UIPropertyMetadata("Test1"));
public Window1()
{
InitializeComponent();
TestColor = new ObservableCollection<string>();
DataContext = this;
TestColor.Add("Red");
TestColor.Add("Orange");
TestColor.Add("Yellow");
TestColor.Add("Green");
TestColor.Add("Blue");
}
}
}
Can anyone explain to me how will I accomplish this using a dependency property? Somehow I am very confused with the Dependency Property concept, and I just wanted to see a working example for that.

You'll need to have your ListBox "select" the current text:
<StackPanel>
<!-- Add selected item binding -->
<ListBox
x:Name="lbColor" Width="248" Height="56"
ItemsSource="{Binding TestColor}"
SelectedItem="{Binding Test}"
/>
<StackPanel>
<!-- No need for elementname - just use Test on the DataContext -->
<Label Content="{Binding Path=Test}" />
</StackPanel>
</StackPanel>

I like to think of Data Binding as Big Brother. The Binding system sets itself up to watch all of its various registered Bindings and when the proper criteria have occurred (for example, FocusLost or PropertyChanged), the Binding system copies the source value to the target. For a TwoWay or OneWayToSource binding, the Binding system will even copy from the target to the source, if the right criteria happen.
The target has to be a DependencyProperty, as this is a special kind of property that knows how to Depend on other values. What the XAML {Binding} is doing under the covers is creating a new BindingExpression and then calling BindingOperations.SetBinding, which registers a particular BindingExpression with the Binding System, so it knows how to watch and perform the updates.
The advantage of this is that neither the target nor the source needs to take the responsibility for writing code to explicitly update the other. If I have code that knows how to provide a list of Colors, why should I care how the Colors get represented to the user? Because the Binding System takes care of the binding, it doesn't matter to me if the target using my Colors is a listbox, a treeview, or any other object that knows how to handle a list of items. I can focus on what I care about (the public interface I'm exposing) and don't have to care about the messy details of gluing that interface to something else.

Related

WPF C# Binding Data from another Class

So I'm missing something simple or losing my mind. I am trying to reuse a class for multiple pages in a WPF application and bind the properties to the pages that instance it. I've tried setting the DataContext but I'm missing something. I'm loading the StockAnalysis page and then creating instance of the PriceChart class (this is the class for reuse) and I want the properties set in the PriceChart class to be the data to bind to the Stock.xaml.cs page. Even in setting the DataContext it is still looking for the StockAnalysis object. Why?
Stock.xaml.cs
public partial class StockAnalysis : Page
{
PriceChart PChart = new PriceChart();
public StockAnalysis()
{
InitializeComponent();
//Load The Data
List<Stock> HistoricalPrice = Database.GetPrices(ticker);
//Create The Charts
this.DataContext = PChart;
PChart.ShowPriceChart(HistoricalPrice);
}
}
Stock.xaml (Look at the Last TexBlock for the Binding of "LastPrice")
<Page x:Class="Stock.StockAnalysis"
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:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
xmlns:local="clr-namespace:Stock"
mc:Ignorable="d"
d:DesignHeight="1000" d:DesignWidth="1200"
Title="Stock Analysis">
<StackPanel x:Name="LastClosePanel" Grid.Row="0" Grid.RowSpan="2" Grid.Column="5" Height="60" VerticalAlignment="Top" Margin="1,0,0,1" Style="{StaticResource LastCloseBackground}">
<TextBlock x:Name="LastCloseText" Foreground="OrangeRed" FontSize="12" HorizontalAlignment="Center" Margin="0,10,0,8">Last Close</TextBlock>
<TextBlock x:Name="LastCloseBind" Foreground="White" FontSize="16" HorizontalAlignment="Center" Text="{Binding LastPrice}"></TextBlock>
</StackPanel>
</Page>
PriceChart.cs (This is where I assign "LastPrice" in hopes to bind it to the TextBlock in stock.xaml.cs)
public class PriceChart
{
public string LastPrice { get; set; }
public void ShowPriceChart(List<Stock> FullList)
{
LastPrice = FullList[0].LastPrice.ToString("C");
//DO OTHER THINGS
}
}
The problem is that PriceChart doesn't implement any change notification. With the current code, this is how things will go when StockAnalysis gets created:
InitializeComponent() will create the TextBlocks and the binding. At this point, DataContext is null, so the binding will fail and the TextBlock stay empty.
this.DataContext = PChart will trigger a binding update (because DataContext is a DependencyProperty, which means it does support change notification). When the binding updates, it will pull the value of LastPrice, which is currently still empty.
ShowPriceChart will set the value of LastPrice, but because PriceChart doesn't support change notification, the binding doesn't know it needs to update, so the TextBlock stays empty.
To solve this, I would recomend your PriceChart implement the INotifyPropertyChanged interface per this article: How to: Implement Property Change Notification.
(Technically, moving PChart.ShowPriceChart(HistoricalPrice) before this.DataContext = PChart would also "solve" the problem, but only if you never need to update the bindings again after initialization.)

WPF user control properties not binding or updating

I'm messing around with WPF and creating User Controls but having a hard time understanding how the databinding is supposed to work. Data binding seems to be overly complex and as long as WPF has been out I would think MS would've created some shortcuts to prevent having to do so much boilerplate code.
User control xaml
<UserControl x:Class="WPFTest.FancyBox"
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">
<DockPanel>
<Label Content="{Binding MyText}"></Label>
</DockPanel>
</UserControl>
User control .cs
public partial class FancyBox : UserControl
{
public static readonly DependencyProperty MyTextProperty = DependencyProperty.Register("MyText", typeof(string), typeof(FancyBox), new PropertyMetadata(null));
public string MyText
{
get => (string)GetValue(MyTextProperty);
set => SetValue(MyTextProperty, value);
}
public FancyBox()
{
InitializeComponent();
}
}
Usage in my main window
<StackPanel>
<local:FancyBox MyText="testing!"/>
</StackPanel>
The binding Content="{Binding MyText}" is binding to the DataContext of the control (Label) which is inherited from closest ancestor up the tree who have one (your code doesn't show any DataContext assignment)
Your intended behavior is for Label's Content to bind to the User Control's Property in this case you need to make the user control your source. Many ways to do this for example:
<UserControl x:Class="WPFTest.FancyBox"
x:Name="RootElement"
....
<Label Content="{Binding MyText, Source={x:Reference RootElement} />
Or another way:
<Label Content="{Binding MyText, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:FancyBox}}" />
Keep in mind any Bindings without a source (Source, RelativeSource) will source from DataContext.
I guess I didn't need data binding for this at all
I changed my controls label to:
<Label x:Name="lblText"></Label>
and my code-behind to:
public string MyText
{
get => lblText.Content.ToString();
set => lblText.Content = value;
}

DataTemplate in Custom Control

I have a custom control (NavigationContentCtrl) for displaying various Views/ViewModels.
In the Resources of the custom control, the data template for a given ViewModel points to the corresponding View (for simplicity I have included only one VM/V pair):
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vm:SampleMainContentViewModel}">
<views:SampleMainContentView/>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
There is also a ContentPresenter bound to an underlying Dependency Property:
<ContentPresenter Grid.Column="1"
Content="{Binding CurrentContentViewModel,
RelativeSource={RelativeSource TemplatedParent}}"/>
In the backing Dependency Property (CurrentContentViewModel), if I instantiate a VM in the property, then the control finds the corresponding View and displays it correctly.
For example, using "SampleMainContentViewModel", this works fine:
// Using a DependencyProperty as the backing store for CurrentContentViewModel. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CurrentContentViewModelProperty =
DependencyProperty.Register("CurrentContentViewModel", typeof(object),
typeof(NavigationContentCtrl),
new FrameworkPropertyMetadata(new SampleMainContentViewModel(),
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
However, if I try to set the VM when I am using an instance of the custom control (e.g. in the MainWindow):
<Grid>
<controls:NavigationContentCtrl
CurrentContentViewModel="{x:Type vm:SampleMainContentViewModel}"
/>
</Grid>
Then all I get is the string name of the VM:
Many thanks to anyone who can help.
Thanks to #Clemens for prompting my brain-reboot.
In the instantiation of the control (e.g. in the MainWindow), the control's dependency property should be bound to property:
<Grid>
<controls:NavigationContentCtrl
CurrentContentViewModel="{Binding ContentViewModel}"
/>
</Grid>
The property, ContentViewModel, is declared and set in the underlying MainWindowViewModel:
public object ContentViewModel { get; set; } = new SampleMainContentViewModel();
Further, the Dependency property can be simplified to remove the BindsTwoWayByDefault syntax and the ContentPresenter's Content property can be set using the simpler TemplateBinding syntax.

How to bind a DependencyProperty of a UserControl to a property in it's ViewModel in MVVM?

The question is not about how to get the stuff working, it already does; it's about some strange behavior I'm experiencing, and I need to understand it. I have a ResourceDictionary that contains some styles, one of them got TargetType="{x:Type UserControl}" and x:Key="UCStyle"; that one is applied on multiple UserControls in the project.
Some of these UserControls got string State property in their ViewModel to be used to apply Visual States (through an external class, and an attached property, bound to the ViewModel in XAML). Till this point everything was perfect, then, I tried to add DependencyProperty State to the UserControl, and simply bind it to the state property in the ViewModel, my attempt was:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--ResourceDictionary Source="..."/-->
</ResourceDictionary.MergedDictionaries>
<Style x:Key="MyStyle" TargetType="{x:Type local:MyUserControl}" BasedOn="{StaticResource UCStyle}">
<Setter Property="State" Value="{Binding State, Mode=TwoWay}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<UserControl.Style>
<DynamicResourceExtension ResourceKey="MyStyle" />
</UserControl.Style>
This worked perfectly at the runtime, but in the design-time, it always underline these lines
And shows an error says:
'MyUserControl' TargetType doesn't match type of element 'UserControl'.
And doesn't apply neither UCStyle nor MyStyle in the XAML Viewer in Visual Studio, and doesn't even draw the child UserControls properly. I didn't expect the solution to run properly, but it did!
Now my questions are:
Why does it show these errors in the design-time while it runs properly?
How to get rid of these errors in the design-time? (I cleaned, and re-built the solution, and restarted Visual Studio, and none of these worked)
What's the best practice to deal with `UserControl` Visual States in such situation in MVVM?
What's the best practice to bind a DependencyProperty of a UserControl to a property in it's ViewModel in MVVM?
I'm using Visual Studio 2012.
The wpf designer is nefarious for showing bogus errors at design time. You can't do much but ignore them.
Visual states are a concern of the UI, and therefore should be contained within the UI. MVVM does not mean no codebehind. Use your codebehind for UI tasks, and put your business logic in your view models.
Your question suggests you're creating custom view models to hold view logic for your user controls. Seriously, don't do that. That'll get you in trouble down the road. It interferes with how databinding is designed to work.
There is no "best practice" for binding user control elements to properties defined on its surface. It depends. Using a style to do this seems odd, however. You can simply give the root of the UserControl an x:Name="root" and then use ElementName=root in your binding.
An example of binding within a UserControl to a property defined on the UserControl (taken from an old prototype)...
Here's a UserControl designed to add or delete a list of stuff.
DependencyProperties defined on the UserControl
Bindings within the UserControl that bind to these properties
I don't guarantee this works, but it will illustrate how it's done:
public partial class ItemsEditor : UserControl
{
#region Items
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register(
"Items",
typeof(IEnumerable<Item>),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public IEnumerable<Item> Items
{
get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
#endregion
#region AddItem
public static readonly DependencyProperty AddItemProperty =
DependencyProperty.Register(
"AddItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand AddItem
{
get { return (ICommand)GetValue(AddItemProperty); }
set { SetValue(AddItemProperty, value); }
}
#endregion
#region RemoveItem
public static readonly DependencyProperty RemoveItemProperty =
DependencyProperty.Register(
"RemoveItem",
typeof(ICommand),
typeof(ItemsEditor),
new UIPropertyMetadata(null));
public ICommand RemoveItem
{
get { return (ICommand)GetValue(RemoveItemProperty); }
set { SetValue(RemoveItemProperty, value); }
}
#endregion
public ItemsEditor()
{
InitializeComponent();
}
}
It just lists a bunch of things, you can add a new thing or delete a thing from the list. Here's the bindings in xaml
<UserControl x:Class="LolPrototype.ItemsEditor"
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:t="clr-namespace:UCsAndICommands"
x:Name="root">
<UserControl.Resources>
<DataTemplate DataType="{x:Type t:Item}">
<StackPanel Orientation="Horizontal">
<Button Command="{Binding RemoveItem, ElementName=root}"
CommandParameter="{Binding}">Remove</Button>
<TextBox Text="{Binding Name}" Width="100"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Button Command="{Binding AddItem, ElementName=root}">Add</Button>
<ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
</StackPanel>
</UserControl>
Obviously, you can define DataTemplates outside the list in an ancestor's resources. The point is to show how ElementName bindings can be used to bind against properties defined in the UserControl.

Setting ViewModel's Property from XAML

I have some UserControl, It's DataContext is binded to the ViewModel,
How to set a ViewModel's property from XAML? Is it possible?
UPD :
Sorry for being not very clear,
I'm trying to get something like this :
UserControl's DataContext is binded to ViewModel, I need to set ViewModel's property to something (let's say, UserControl's Width property).
Is it possible?
UPD2: It seems to be not possible.I know about TwoWay binding mode, etc, thing I wanted to do - to set ViewModel's property to UserControl's one
This example should be very clear
<Set Property={Binding SomePropertyOnViewModel}
Value={Binding RelativeSource={RelativeSource Self},
Path=SomePropertyOnUserControl}>
I am not sure whether I understand the question exactly.
But here is an example.
It will:
Create a view model of type ExampleViewModel inside the user control by setting the user
controls DataContext property in xaml
Create a text box in xaml and bind it to the view models
TextInViewModel string property.
Set up the usual INotifyPropertyChanged interface (this was extracted to the base class ViewModelBase)
Create the view model in xaml and set the user controls data context to it:
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Test"
xmlns:viewModel="clr-namespace:ViewModels">
<UserControl.DataContext>
<viewModel:ExampleViewModel/>
</UserControl.DataContext>
<StackPanel Orientation="Horizontal" >
<Label>Enter Text here: </Label>
<TextBox Text="{Binding TextInViewModel}"></TextBox>
</StackPanel>
</UserControl>
ViewModel:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string prop)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
}
public class ExampleViewModel : ViewModelBase
{
/// <summary>
/// Property bound to textbox in xaml.
/// </summary>
public String TextInViewModel
{
get { return _textInViewModel; }
set
{
_textInViewModel= value;
RaisePropertyChanged("TextInViewModel");
}
}
private string _textInViewModel;
/// <summary>
/// Constructor.
/// </summary>
public ExampleViewModel()
{
}
}
Binding works both ways: i.e. from source (e.g. viewmodel) to target (e.g. usercontrol) and from target back to source.
You specify the direction via Mode of binding.
Following are the BindingModes:
TwoWay
OneWay
OneTime
OneWayToSource
In your case, if you want to bind width property of usercontrol to the TheWidth property of ViewModel:
Case A:
Want to bind in both directions, use Mode=TwoWay
<UserControl Width="{Binding TheWidth, Mode=TwoWay}">
<!-- your rest of code -->
</UserControl>
Case B:
Want to bind only from usercontrol to viewmodel, use Mode=OneWayToSource
<UserControl Width="{Binding TheWidth, Mode=OneWayToSource}">
<!-- your rest of code -->
</UserControl>
XAML
<UserControl.DataContext>
<vm:ViewModel/>
</UserControl.DataContext>
I prefer the ViewModel Locator approach (this is like a service locator pattern for ViewModel).
Because as soon as your ViewModel has constructor parameters, you are either tightly coupled, or you can't use the above described xaml way....
There are many ViewModel-Locator ways. One is described here using MEF and silverlight.
http://johnpapa.net/simple-viewmodel-locator-for-mvvm-the-patients-have-left-the-asylum
here is another one:
http://brendan.enrick.com/post/Wire-up-your-ViewModels-using-a-Service-Locator.aspx
Well, you bind your UI elements to them:
<UserControl Width="{Binding Path=DisplayWidth, Mode=OneWayToSource}">
<Grid>
<TextBox MinWidth=100 Text="{Binding MyProperty}"/>
</Grid>
</UserControl>
assuming a view model like this:
class ViewModel
{
public string MyProperty { get; set; }
public int DisplayWidth { get; set; }
}
Through binding my dear friend..
for example: (Assuming in your context)
If you have class "Person" and your person has a Name and SurName public property and you want to bind it to a textbox. You do the following:
<TextBox Text="{Binding Path=Name}" />
This only works if the name is your public property, it is best practice to make you object ( in this case Person) as a public property and use the Path parameter differently.
Example:
<TextBox Text="{Binding Path=Person.Name}" />
It does clutter your code way less, then to make a property in your viewmodel for every property of any object in your viewmodel.
"How to set a ViewModel's property from XAML? Is it possible?"
So, that seems to be not possible, max you can accomplish - two-way binding, which is, unfortunately not I wanted.
All in all it's rather bad design than a problem

Categories