WPF Binding to UserControl Custom DependencyProperty - wpf

I have a custom UserControl called SongDescription:
<UserControl x:Class="DPTestAp.SongDescription" ...>
<Grid x:Name="LayoutRoot">
<DockPanel Height="50">
<TextBlock x:Name="title" Text="{Binding name}" Width="100" Height="30"/>
<TextBox x:Name="lyrics"/>
</DockPanel>
</Grid>
</UserControl>
I added DependencyProperty to it:
public partial class SongDescription : UserControl
{
public static readonly DependencyProperty SongProperty = DependencyProperty.Register("Song", typeof(Song), typeof(SongDescription));
public Song Song
{
get
{
return (Song)GetValue(SongProperty);
}
set
{
SetValue(SongProperty, value);
updateLyrics()
}
}
private void updateLyrics()
{
lyrics.Text = Song.lyrics;
}
public SongDescription()
{
InitializeComponent();
}
}
The question is: how to bind something to this SongProperty?
I use SongDescription in my main window like this:
<local:SongDescription x:Name="songDescription" Song="{Binding DataContext}"/>
I cannot make my TextBox lyrics show lyrics. In main window I tried to set DataContext to songDescription, like this:
songDescription.DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };
or to window itself like this:
DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };
I even tried to make Song a resource and bind it to SongProperty like this:
<Window.Resources>
<local:Song x:Key="res" name="Home" lyrics="Hold on, to me as we go"/>
</Window.Resources>
<Grid>
<local:SongDescription x:Name="songDescription" Song="{StaticResource res}"/>
</Grid>
Nothing helped. TextBlock title binds song name fine. But I can't make updateLyrics() method be called. (In real life this method is more complicated, so I can't use Binding like with name).
Thank you!

Yup, so that's a gotcha with dependency properties. You never ever put validation code inside of the accessor methods (get/set) because dependency properties are stored by WPF in a table that it itself manages. This is why you have to register dependency properties, it essentially creates entries on this table for storing the values associated with each property, and when you use 'GetValue' / 'SetValue' you are updating the entries on this table (which by the way relates to how WPF is able to manage data bindings in general).
The upshot of this though is that WPF can (and will) completely bypass your property accessors because it has direct access to the real data. Why should it use your accessors if it can just go to the data directly. Instead you need to implement a 'PropertyChanged' callback function or some WPF sanctioned method of doing validation, but never ever do it in your accessors.
See:
http://msdn.microsoft.com/en-us/library/ms752914.aspx

In addition to sircodesalot's answer, you are not bound on your lyrics textbox. Also, since the song your bound to is a class, you will need to specify the paths fully for the properties you want to show in the boxes such as "Path=Song.Lyrics".
Another thing to consider is that with dependency properties; your mode will be oneway by default so making the text field editable would be moot really unless you change it.
Third, if you're using MVVM you only need your main window context to be set to the view model and have a matching Song property to bind against.

Related

How to show ContentControl in designer when using Calibrun.Micro viewModel-first approach?

I'm using Caliburn.Micro (CM) in a WPF application with ViewModel-first approach. I'm composing the main view with a command bar and an active item. Main viewModel sets the property for the command bar viewModel, and navigates to active item correctly.
Everything looks fine at runtime, the issue is only related to design-time: the main view shows empty in designer and I cannot find how to set it correctly. I managed to having this working in other scenarios, e.g. when setting the datacontext at design time for a whole Window or UserControl, i.e. when that's the root UI element in XAML. But now I'm not able to to this for child ContentPresenter UI elements within a Window.
This is an excerpt of the main view I'm composing:
<Window x:Class="...MainView" ...>
<DockPanel ...>
<!-- this one binds to a property of type CommandBarViewModel -->
<ContentControl x:Name="CommandBar" ... />
<ContentControl x:Name="ActiveItem" ... />
</DockPanel>
</Window>
I've checked a number of related reads, but none of them seems to fit/solve my issue. This question is basically the same as mine, but has no answers. That has a reference to this other question which it seems to me is going for a View-first approach, judging by the cal:View.Model bindings.
I tried adding a design-time context like the following (fake namespace not shown for brevity):
<ContentControl x:Name="CommandBar" ...
d:DataContext="{d:DesignInstance Type=fake:DesignTimeCommandBarViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True"/>
but then I incur in one of two cases:
if DesignTimeCommandBarViewModel inherits from the actual CommandBarViewModel, then I incur in somewhat the usual problem of design-time Vs dependency injection: the default constructor passes null for all injected dependencies, and base constructor or something else gives problem. I mean, it seems it would take some effort to find a workaround for this, and just for design-time support
if DesignTimeCommandBarViewModel does not inherit from the actual viewModel, then it seems that (correctly) the CommandBarView is not instantiated, as now there's no relationship anymore between the viewModel and that view.
Have you got any idea about this? Maybe this should be solved with a design-time version of the hosting MainViewModel?
Other references I checked: this answer, from Rob Eisenberg himself, this CM thread, this other SO
Edit
Following my last (auto-)hint, I'm trying also creating and instantiating a DesignTimeMainViewModel, not inheriting from MainViewModel, which exposes the same properties and sets a DesignTimeCommandBarViewModel in its default constructor. In this case, in place of the command bar the designer shows the classic CM complaint: cannot find view for the DesignTimeCommandBarViewModel.
What's next?
Well, here's the solution I found: I'd be glad to hear about better ways or other suggestions.
Host MainView XAML specifies a design-time data-context pointing to a design-time version of the Main view-model which, by the way, does not inherit from the runtime version MainViewModel. ContentControl items are left untouched.
<Window x:Class="...MainView" ...
d:DataContext="{d:DesignInstance Type=fake:DesignTimeMainPanelViewModel, IsDesignTimeCreatable=True}"
cal:Bind.AtDesignTime="True">
<DockPanel ...>
<ContentControl x:Name="CommandBar" ... />
<ContentControl x:Name="ActiveItem" ... />
</DockPanel>
</Window>
DesignTimeMainPanelViewModel has the same public properties as MainPanelViewModel, has a default c'tor without dependencies and its c'tor sets the CommandBar property to a new instance of DesignTimeCommandBarViewModel:
public class DesignTimeMainPanelViewModel
{
public DesignTimeMainPanelViewModel()
{
CommandBar = new DesignTimeCommandBarViewModel();
ActiveItem = ...some instance here as well...;
}
public DesignTimeCommandBarViewModel CommandBar { get; private set; }
public IScreen ActiveItem { get; private set; }
}
DesignTimeCommandBarViewModel class is decorated with a custom Attribute having only one required parameter, the System.Type of the view associated with that view-model.
During bootstrap the code adds a new ViewLocator strategy to get the view Type from the view-model Type, by setting a new ViewLocator.LocateTypeForModelType.
The new locator function will try to find a view Type if the standard locator function cannot find one. Granted, it will look for the custom attribute on view-model Type, and if found that would be the returned view Type. Here's the gist of that:
Type viewType = _previousLocate(viewModelType, displayLocation, context);
if (viewType == null)
{
FakeViewAttribute fakeViewAttr = Attribute.GetCustomAttribute(viewModelType, typeof(FakeViewAttribute)) as FakeViewAttribute;
if (fakeViewAttr != null) viewType = fakeViewAttr.ViewType;
}
return viewType;

How to use ArcGIS for WPF custom symbol with data binding?

Esri's ArcGIS for WPF does not seem to have any real MVVM support or binding support; unless I'm mistaken.
Anyway, I am creating a tracking application and it needs to track objects via GPS coordinates.
I have implemented a custom IGeoPositionWatcher<GeoCoordinate> which is used with the GpsLayer. This all works perfectly. I can modify the GPS coordinates and my little dot on the map moves smoothly to it's final resting place. My problem is that I can't use the little dot and arrow that comes with the GpsLayer. I need a custom MarkerSymbol to be something similar to the following:
<Grid>
<Image Source="{Binding Pogostick.HeightImage}" />
<TextBlock Text="{Binding Pogostick.PogoId}" />
</Grid>
I have a list (amount unknown and changes at any given moment) of these "pogosticks" which are always tracking by GPS and need to have the symbol update according to its status. The problem is that I can't get the Pogostick object to be the DataContext for the custom MarkerSymbol.ControlTemplate and there for my image source and text do not show.
I'm trying to avoid using a Graphic in a GraphicLayer and would like this to work with the GpsLayer. Is there a way to do this at all? Am I even approaching this correctly... should I be using a GraphicLayer or a FeatureLayer?
I want to render these tracked pogosticks on the user's desktop app and not by editing layers or features on the map service. Perhaps I should be using a custom renderer?
I've figured out a way to do this. I created a class which inherits from MarkerSymbol which has a DependencyProperty which holds the object I want to bind to (the Pogostick class).
public class EntityMarkerSymbol : MarkerSymbol
{
public static readonly DependencyProperty EntityProperty;
static EntityMarkerSymbol()
{
EntityMarkerSymbol.EntityProperty = DependencyProperty.Register("Entity", typeof(object), typeof(EntityMarkerSymbol), new PropertyMetadata());
return;
}
public EntityMarkerSymbol()
{
return;
}
public object Entity
{
get { return this.GetValue(EntityMarkerSymbol.EntityProperty); }
set { this.SetValue(EntityMarkerSymbol.EntityProperty, value); }
}
}
Then I create a ControlTemplate in a resource dictionary like so:
<ControlTemplate x:Key="PogostickMarker">
<StackPanel>
<Image Source="{Binding Symbol.Entity.HeightImage}" />
<TextBlock Text="{Binding Symbol.Entity.PogostickId}" />
</StackPanel>
</ControlTemplate>
The key to this working is that ArcGIS for WPF will automatically assign the MarkerSymbol's DataContext to it's sealed class DataBinding which contains a reference to the MarkerSymbol assigned to the GpsLayer's LocationMarkerSymbol. So I can access my custom symbols Entity property through this binding.
The code used to create the GpsLayer and assign the MarkerSymbol is below:
GpsLayer layer = new GpsLayer();
EntityMarkerSymbol marker = new EntityMarkerSymbol() { Entity = pogoStick };
marker.ControlTemplate = Application.Current.Resources["ConvoyMarker"] as ControlTemplate;
layer.LocationMarkerSymbol = marker;
NOTE: this approach cannot be used when the UseAcceleratedDisplay property of the map is set to True. If someone can figure out how to use this approach with that property set to true, please let me know.

WPF VB.NET - Reference Controls in MainWindow from a Module

I want to use modules in my first WPF application, as I'm used to using them in WinForm applications I've created before. So I have this button, with a textblock inside.
MainWindow.xaml:
<Button x:Name="Connection_Button" Width="200" Height="30" Margin="5,5,5,5">
<TextBlock FontSize="14" MouseDown="TextBlock_MouseDown">CONNECT</TextBlock>
</Button>
Before in WinForms I could easily reference a button and it's text property by adding MainForm. before the control, but how can I do this in WPF through modules, similar to below? How do I even declare the controls at the top of my module code? And elements inside a control such as TextBlock?
Module_Connection.vb in my old WinForm way:
Private Sub Connect()
If MainForm.Connection_Button.Text = "Connect" Then
' Code
You don't usually do this in WPF.
To base your application logic in the state of UI elements is not really a good idea.
Why? because UI is Not Data and therefore you should base your application logic on the state of data items instead.
I suggest you read this and this
To try to use WPF the same way you use winforms is a straight path to miserable failure and suffering.
You must embrace MVVM and understand DataBinding and how the UI must always be separate from Application Logic and Data.
Edit: Adding a Code Sample (in C#)
ViewModel:
public class MainViewModel
{
public string ButtonContent {get;set;} //TODO: Add Property Change Notification
public MainViewModel()
{
ButtonContent = "SomeButtonContent";
}
}
View:
<Button Width="200" Height="30" Margin="5,5,5,5">
<TextBlock FontSize="14" Text="{Binding ButtonContent}"/>
</Button>
Code Behind:
public class MainWindow: Window
{
public MainWindow()
{
InitializeComponent();
SomeModule.Instance = new MainViewModel();
DataContext = SomeModule.Instance;
}
}
Module (static class in C#):
public static class SomeModule
{
public static MainViewModel Instance {get;set;}
public void DoSomething()
{
if (Instance.ButtonContent == "SomeButtonContent")
//...etc
}
}
This is what I mean by separating the UI from the data. You place your strings in a ViewModel, not in the View, then you evaluate the value of those ViewModel properties to determine what to do.
Still, basing your application logic on the value of a string seems like a weak solution. What do you really need to do? There are much better approaches such as using an Enum for this.
Edit2:
Having a ViewModel to Bind to does not "complicate" things. It actually simplifies them a LOT.
How? because you're now doing this with simple controls, but then you'll want to do the same thing with UI elements inside a ControlTemplate or even worse a DataTemplate and that's when real problems arise.
Therefore, you must get used to "the WPF Way" before you deal with more complex UI scenarios.
Non-optimal approach:
public class MainWindow: Window
{
public string ButtonContent
{
get
{
return this.txtButtonContent.Text;
}
set
{
this.txtButtonContent.Text = value;
}
}
}
<Button Width="200" Height="30" Margin="5,5,5,5">
<TextBlock x:Name="txtButtonContent" FontSize="14" Text="Connect"/>
</Button>
You must understand that the Button class doesn't have a Text property in WPF. In contrast to most ancient framweworks, WPF has a Content Model where literally anything can contain anything with little to no restrictions. Putting a Text property to the Button would be to introduce the limitation for the Button to only contain Text, which is not the case in WPF.
Therefore, what you actually want to do here is to modify the Text property of the TextBlock (which happens to be inside the Button, but could actually be anywhere in the Visual Tree).
That's why I mentioned the fact that you actually NEED a ViewModel to hold your data, because there is no (easy) way to access the UI elements located, for example within a DataTemplate in order to manipulate them. Your UI must always be a reflection of the state of your application's Data, which is stored in Model or ViewModel classes, not the UI.
This should get you in the ballpark:
Module:
Public Sub Connect(RyRef txtBox as TextBox)
If txtBox.Text = "...
note Public vs. Private
Call:
Call .Connect(MyTextBox1)
Call and are optional
I'll answer your question in VB.NET and not provide C# samples of alternative ways of doing it which if you're not used too can be an uphill struggle.
Declare this in your module, access the controls through rootWindow.
Private rootWindow As MainWindow = TryCast(Application.Current.MainWindow, MainWindow)
Another approach is :
Dim mw as MainWindow = Application.Current.Windows.OfType(Of MainWindow).First
assuming you have a MainWindow and only have one MainWindow.

GraphSharp library - binding layout

In our project we are using GraphSharp library. We have encountered some problems when we wanted to remove all edges and vertices from graph.
In every example there is, in xaml there is something like that
<zoom:ZoomControl Grid.Row="1" Zoom="0.2" ZoomBoxOpacity="0.5" Background="#ff656565">
<toProjectGraph:EntityGraphLayout x:Name="graphLayout" Margin="10"
Graph="{Binding Path=GraphViewModel.EntityGraph}"
LayoutAlgorithmType="{Binding Path=GraphViewModel.LayoutAlgorithmType, Mode=OneWay}"
OverlapRemovalAlgorithmType="FSA"
HighlightAlgorithmType="Simple"
/>
</zoom:ZoomControl>
xaml creates instance of our class EntityGraphLayout and uses it to visualize everything.
Is it possible in some way to "bind" this instance of EntityGraphLayout to some property in our view model so we can reference it in our view model code?
Or maybe there is a way that we can create instance of this class and tell xaml to get referebce to object from some path.
Sounds like what you want is to create the object in your viewmodel, expose it as a property, and bind it to the Content property of your zoom control, something like this:
viewmodel:
public class ViewModel {
private EntityGraphLayout _layout = new EntityGraphLayout();
public EntityGraphLayout EntityGraphLayoutProperty
{
get { return _layout; }
set { _layout = value; }
}
}
XAML:
<zoom:ZoomControl Content="{Binding EntityGraphLayoutProperty}" Grid.Row="1" Zoom="0.2" ZoomBoxOpacity="0.5" Background="#ff656565" >
</zoom:ZoomControl>
Note that you will need to make sure the DataContext for the zoom control is set to your viewmodel.
If you want it created in XAML, you could also access the object in your viewmodel by referring to it by the graphLayout name you defined in the XAML. This would require a reference to the view in your viewmodel, which may not be ideal.

How do you pass a ViewModel to the constructor of a UserControl that is shown in a ContentPresenter?

I have several UserControls that should display the same data. Each UserControl has a different layout of the data that is to be presented. The ContentPresenter can bind to any one of the UserControls by using a DataTemplate in my Resources and by binding the Content to a StyleViewModel. Each UserControl is associated with a ViewModel as defined in the DataType of the DataTemplate. The ViewModels associated with any given UserControl all inherit from the StyleViewModel. The UserControls should get their data from a SettingsViewModel. The UserControls appear in the main Window.
The problem is that I can't figure out how to make the data from the SettingsViewModel accessible to the UserControls.
Is it possible to pass a reference to a SettingsViewModel to the constructor of one of these UserControls that are displayed using a ContentPresenter?
Is there another way to easily switch between different views of the data (i.e. my UserControls) without using a ContentPresenter? If so, how would I make the data accessible to the UserControls?
The following is code from my SingleLineViewModel.cs:
public class SingleLineViewModel : StyleViewModel
{
public SingleLineViewModel() { }
}
The other ViewModels are similar. They are essentially empty classes that inherit from StyleViewModel, so that I can bind to a Style property which is of type StyleViewModel in my SettingsViewModel. The StyleViewModel is also an essentially empty class that inherits from ViewModelBase.
The following is code from my Resources.xaml:
<ResourceDictionary <!--other code here-->
xmlns:vm="clr-namespace:MyProject.ViewModel"
<!--other code here-->
<DataTemplate DataType="{x:Type vm:SingleLineViewModel}">
<vw:ucSingleLine/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SeparateLinesViewModel}">
<vw:ucSeparateLines/>
</DataTemplate>
<!--other code here-->
</ResourceDictionary>
The following is code from SettingsViewModel.cs:
public class SettingsViewModel : ViewModelBase
{
// other code here
private StyleViewModel _style;
public StyleViewModel Style
{
get { return _style; }
set
{
if (value != _style && value != null)
{
_style = value;
OnPropertyChanged("Style");
}
}
}
// other code here
public SettingsViewModel()
{
_style = new SingleLineViewModel();
}
// other code here
}
The following is code from my MainView.xaml:
<ContentPresenter Name="MainContent" Content="{Binding SettingsVM.Style}"/>
You might find that you are trying to do much at once. Consider how you might test this scenario? Or how would you walk the data in a Debugger to check its state? Good practice recommends that your data is separate from your UI elements. An MVVM pattern such as you are trying to use normally provides the view models to help transition the data from it's simple data into forms that the UI can use.
With that in mind, I would recommend that you try do develop a ViewModel tier that presents all the data without the UI holding it together, i.e. instead of trying to inject the additional SettingsViewModel into your controls you should make your viewmodels hold everything they need.
It looks like you are off to a good start, your SettingsViewModel lets you get hold of a Style, but your style doesn't seem to have any data. So why not pass it in the constructor.
public SettingsViewModel()
{
_style = new SingleLineViewModel(WhatINeedForStyle);
}

Resources