Create StaticResource from DataContext? - wpf

All,
Is it possible to create a StaticResource from an object in DataContext (without added code-behind)? Take for example a DependencyProperty of a UserControl:
public static DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(IVMHeaderGeneric), typeof(UIHeader), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public IVMHeaderGeneric ViewModel
{
get
{
return (IVMHeaderGeneric)this.GetValue(ViewModelProperty);
}
set
{
this.SetValue(ViewModelProperty, value);
}
}
IVMHeaderGeneric is an interface that is instantiated as a class by the consumer of this user control.
What I need to do is, somehow (preferably without code-behind), add this to the UserControl's Resources, thus allowing me to perform data-bindings on UIElements that do not inherit DataContext (i.e. the DataGridColumn comes to mind).
Thanks in advance.

I think that you could not create an instance the interface in the xaml resources, because, as you said, the implementation is out of UserControl scope.
Instead of creating StaticResource, you can use Binding to refer to the UserControl DataContext property. For example, if you give the name of your root element, Root, you can write the following:
<DataGridColumn SomeDependencyProperty="{Binding ElementName=Root, Path=ViewModel.Property}" />

Related

UserControl enum DependencyProperty not binding

I created a UserControl that contains 3 DependencyProperties. Two are working fine, but there is one that gives me a real headache.
I have an enum (outside the UserControl class but same namespace):
public enum RecordingType
{
NoRecording,
ContinuesRecording,
EventRecording
}
I created a DependencyProperty for it as follows:
public static DependencyProperty SelectedRecordingTypeProperty = DependencyProperty.Register("SelectedRecordingType", typeof(RecordingType), typeof(SchedulerControl),
new FrameworkPropertyMetadata((RecordingType)RecordingType.NoRecording, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public RecordingType SelectedRecordingType
{
get
{
return (RecordingType)GetValue(SelectedRecordingTypeProperty);
}
set
{
SetValue(SelectedRecordingTypeProperty, value);
}
}
and I'm using it in XAML like this:
<userControls:SchedulerControl
Grid.Row="1"
Grid.Column="3"
SelectedRecordingType="{Binding CurrentRecordingType,UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}"
FullRecordingSchedule="{Binding MondayFullRecordingSchedule,UpdateSourceTrigger=PropertyChanged}"
SelectedRecordingTime="{Binding MondaySelectedRecordingTime,UpdateSourceTrigger=PropertyChanged}"/>
There are two more DependencyProperties that work just fine (I get to their get and set methods inside the UserControl), but this one is just a no-go. I created DPs before and I'm doing everything the same. I also made sure the binding in my VM is ok and the getter and setter are being called correctly.
Any help would be great!
Also I checked that I my VM. The binding does execute.
Let me show an other solution for UserControl (UC from now) with a ComboBox and Enum bindings.
Also a common problem, when you can bind the enum, but you can't get the SelectedItem of the ComboBox from the UC. This solution will also provide the SelectedItem.
For example, I have an ExampleUC : UserControl UC class, which is able to accept an enum, and to provide the SelectedItem. It will do it using properties (attributes in .xaml).
I also have an enum, called ExampleEnum, and the Window, which creates a new instance of ExampleUC and setting that's properties/attributes.
In the ExampleUC.xaml:
<UserControl x:Class="TestNamespace.View.ExampleUC"
xmlns:view="clr-namespace:TestNamespace.View"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:markup="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType={markup:Type view:ExampleUC}}}">
<ComboBox ItemsSource="{Binding EnumTypeArray}" SelectedItem="{Binding SelectedItem}"/>
</Grid>
</UserControl>
As you can see, the DataContext of the UC has been set to it's ancestor's DataContext, which means it can receive the wanted parameters (you can find more explanations about DataContext inheritance and visual tree, just make some researches about them).
The binded properties (EnumTypeArray and SelectedItem) are DependencyProperties in the ExampleUC.xaml.cs file:
public Array EnumTypeArray
{
get { return (Array)GetValue(EnumTypeArrayProperty); }
set { SetValue(EnumTypeArrayProperty, value); }
}
public object SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty EnumTypeArrayProperty =
DependencyProperty.Register("EnumTypeArray", typeof(Array), typeof(ExampleUC), new PropertyMetadata(new string[0]));
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(ExampleUC), new PropertyMetadata(null));
To create new DependencyProperty you can use the propdp code snippet. (Write it and press TAB by default). These properties will be shown as attributes in the .xaml editor, when you create and edit the instances of ExampleUC.
At this stage you have a UC, which can accept an enum, and return the SelectedItem.
The enum somewhere:
public enum ExampleEnum
{
Example1,
Example2
}
The Window, which uses the ExampleUC:
You have to add a new resource to the Window's resources, which will be an ObjectDataProvider in order to be able to use your enum as ItemsSource:
<Window.Resources>
<ObjectDataProvider x:Key="MyEnumName" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
<ObjectDataProvider.MethodParameters>
<x:Type TypeName="local:ExampleEnum"/>
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
Please note that, the local namespace prefix has been defined earlier at the namespaces' section, which is the namespace of ExampleEnum, for example:
xmlns:local="clr-namespace:TestNamespace.Data"
To use ExampleUC, in a Grid or Panel, use the following:
<views:ExampleUC EnumTypeArray="{Binding Source={StaticResource MyEnumName}}" SelectedItem="{Binding MyProperty, Mode=TwoWay}"/>
To set the Mode to TwoWay is necessary to be able to get and set the property.
Please note that, you might have to define the views namespace at the namespaces' section, if Visual Studio wouldn't do it for you.
As you can see, the previously defined DependencyProperties are showing up as attributes. The EnumTypeArray is responsible to fill the ComboBox's items, and the SelectedItem has been binded to MyProperty, which is a property in the model class, such as:
public ExampleEnum MyProperty{
get{ return _myProperty;}
set{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
This example only shows how to use enums through UCs. Since this UC has only a single component (a ComboBox), it's useless in practice. If you decorate it with a Label or others, it would do the job.
Hope it helps.

MVVM + UserControl + Dependency Property

Alright, this is somewhat related to this question: WPF Printing multiple pages from a single View Model
I tried to follow the advice given there but now I am stuck.
My application uses a MainView.xaml and the appropriate MainViewViewModel.cs, I am using MVVM Light in the background.
Now - according to the post - it seems I have to do the following:
Create a user control
Expose some properties from the user control
Make sure the view model shows these properties
The idea is clear but I am stuck when trying to notify each other.
My user control (UcTest.xaml) exposes a Dependency Property:
public string SpecialText
{
get { return (string)GetValue(SpecialTextProperty); }
set
{
SetValue(SpecialTextProperty, value);
}
}
// Using a DependencyProperty as the backing store for SpecialText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SpecialTextProperty =
DependencyProperty.Register("SpecialText", typeof(string), typeof(UcTest), new PropertyMetadata(new PropertyChangedCallback(SpecialTextChangedPropertyCallback)));
private static void SpecialTextChangedPropertyCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Do something
Debug.WriteLine("Ffgdgf");
}
Alright, so I do now have a user control which has some dependency properties. Yet, these properties are completely separated from my ViewModel properties (those are the ones which shall be displayed).
So basically I have two possibilities:
How can I now tell my ViewModel for the UserControl that some properties have changed?
Is there a possibility to forget about the dependency properties and access the view model directly?
Additional info #1:
I have uploaded a (simple) example of what I am trying to do here: Example Project. I would like to change the value of the label in UserControl1 (via the binding property in the ViewModel for UserControl1) from my MainViewViewModel.
You would usually bind the UserControl's property to the ViewModel property. A two-way binding would work in both directions, from ViewModel to View and vice versa.
<Window x:Class="TestApplication.MainWindow" ...>
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
</Grid>
</Window>
To directly access the ViewModel object in the above example, you could simply cast the UserControl's DataContext property to the ViewModel type. The DataContext is inherited from the MainWindow.
var viewModel = DataContext as MyViewModel;
var property = viewModel.MyViewModelProperty;
You could of course also directly assign a specialized ViewModel instance to the UserControl's DataContext:
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
<local:UcTest.DataContext>
<local:UserControlViewModel/>
</local:UcTest.DataContext>
</local:UcTest>
or you may create the ViewModel instance as a resource in a resource dictionary and assign the DataContext like this
<local:UcTest DataContext="{StaticResource MyUserControlViewModel}"
SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
Alright, after hours of googling it appears that the "correct" approach to this is not to do it at all. The general approach is to keep the data in your MainViewModel and not use an additional ViewModel for the UserControl (which I find a little ... well .. not so good). The main problem is that there is no easy mechanism to get the Data from the Dependency Property to the ViewModel.
For printing, I have now gone back to doing it purely in code.

Getting Value from ViewModel through DataContext WITHOUT Binding?

New to WPF. I am creating UserControls that need read access to the ViewModel state to do their thing. I currently use the following technique:
public partial class ControlBar : UserControl
{
private static readonly DependencyProperty URLProperty =
DependencyProperty.Register("URL", typeof(string), typeof(ControlBar),
new UIPropertyMetadata(null));
public ControlBar()
{
InitializeComponent();
SetBinding(URLProperty, "CurrentPage.URL");
Pin.Click += Pin_Click;
}
private void Pin_Click(object sender, RoutedEventArgs e)
{
var URL = (string)GetValue(URLProperty);
}
}
Is this the correct way and is it not overkill to set up a long-term binding for each variable I need access to? Or can you do something like:
GetValue(new Path("CurrentPage.URL.....
I made up the above obviously.
Thanks!
In general data-binding is the way to go. However sometimes when you are creating controls that have view-specific concerns for which data-binding will not be appropriate.
In those cases you will want to be able to interact with the DependencyProperty to set it and know when it changes. I have been following a pattern that I picked up from a Charles Petzold article in MSDN magazine.
My answer to another question shows the pattern for creating a DependencyProperty for a UserControl Stack Overflow: Dependency Property In WPF/SilverLight
Again, data-binding to a view model will likely solve your problem, but a DependencyProperty may come in useful depending on the situation.
Update in response to comment:
In many situations you can data bind your in a UserControl without using a DependencyProperty. For example if you have a TextBlock that displays a name you would put a TextBlock in the XAML of the UserControl
<TextBlock Text="{Binding Path=NameString}" />
In the view model which is present in the DataContext you would have a property NameString and if the TextBlock is to update the display when the NameString property changes the view model should implement INotifyPropertyChanged and the property should fire the PropertyChanged event with the name of the property sent along with the event.
protected string _NameString;
public string NameString
{
get { return _NameString; }
set { _NameString = value: Notify("NameString"); }
}
Where Notify is a method that checks the PropertyChanged event for null and sends the event if not null.
This works well if everywhere that you want to use the UserControl has a view model with a Name property. The great thing is that the UserControl can pick up on the DataContext of wherever it is hosted and bind to an external view model.
When you want to start binding the same UserControl to different properties is one place that you may want to use a DependencyProperty. In that case you could make a UserControl with a DependencyProperty and bind it to different properties
<my:SampleControl NameString="{Binding Path=GivenName}" />
<my:SampleControl NameString="{Binding Path=FamilyName}" />
And then have an internal view model that the DependencyProperty change handler updates when the bound property changes.
Update: No DependencyProperty or binding
You can always add an ordinary C# property to the UserControl and pass the data in that way.
public MyClass Data { get; set; }
Then in the code-behind of the UserControl you can simply use the property:
if (this.Data != null)
{
this.textBox1.Text = Data.NameString;
}
Update in response to comment:
Another way to access the view model in code is to cast the DataContext to your view model type:
MyClass data = this.DataContext as MyClass;
if (data != null)
{
// do something
this.textBox1.Text = data.NameString;
}

Binding to DependencyProperty in UserControl

I have a form with two different UserControls - one that contains a Telerik RadGridView and the other that that contains a Telerik DataForm.
The grid usercontrol is bound to a ViewModel that includes a property that exposes the Items collection that the grid is bound to.
When I bind the form to that property, everything works fine.
But I need to access additional info in the form control that really doesn't belong in the grid control's viewmodel.
So I thought I'd add a property to the form usercontrol, and bind it to the items collection:
<local:FormControl x:Name="formControl"
ItemsSource="{Binding items}"
/>
In the form's code-behind, I added a normal property:
private object itemsSource;
public object ItemsSource
{
get { return this.itemsSource; }
set { this.itemsSource = value; }
}
And this didn't work, of course. I got errors about having to use a DependencyProperty. Which I thought was reassuring - the page was actually trying to bind to the property I thought it should.
So I converted this to a DependencyProperty:
public static DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(FormControl));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
That compiled and ran without errors. Except, of course, that the control's viewmodel didn't have any items. So next was to try to pass the ItemsSource property to the form control's viewmodel:
public FormControl()
{
InitializeComponent();
this.DataContext = new FormControlVM(this.ItemsSource);
...
And this didn't work. ItemsSource was null when FormControl() was constructed. So I added a setItemsSource() method to the viewmodel, and called it in the ItemsSource property's set function. This didn't work, either. The xaml is apparently binding to the property, but it seems to do it without calling the property's set function.
So I decided to listen to the ValueChanged event, on the DependencyProperty:
public FormControl()
{
InitializeComponent();
this.DataContext = new FormControlVM();
DependencyPropertyDescriptor prop =
DependencyPropertyDescriptor.FromProperty(FormControl.ItemsSourceProperty, this.GetType());
prop.AddValueChanged(this, delegate
{
FormControlVM formControlVM = (FormControlVM)this.DataContext;
formControlVM.setItemsSource(this.ItemsSource);
});
...
And it's still not working. The ValueChanged delegate never seems to get called.
This seems like it should be a simple thing, but I've not been able to find examples on-line, and I've tried every combination I can conceive of.
Any ideas as to how I should handle this?
============ Additional info on the binding ============
Will was asking for info on the xaml that does the binding.
If I put this in page that contains the user control, binding the user control to the viewmodel of the page:
<local:FormControl x:Name="formControl"
Grid.Column="2"
DataContext="{Binding}"
/>
And then this in the user control, binding the form in the user control to the itemsCollection of the page's viewmodel:
<telerik:RadDataForm
ItemsSource="{Binding itemsCollection}"
Header="View Item:"
CommandButtonsVisibility="None"
AutoGenerateFields="False"
/>
Then everything works fine.
But the problem is that I can't bind the user control to the viewmodel of the page. I need to have the user control's viewmodel expose information that the page's viewmodel should not be seeing.
And I can't have the form in the user control reaching outside of the user control, binding to a property on the page's viewmodel. It's a violation of encapsulation, that will make using the user control on a different page much more complicated, and severely limit how I might modify the internals of the control in the future. (The page should not know anything about the controls within the user control, the controls within the user control should not know anything about the page.)
When I include the user control on a page, I want to bind the user control to a property of the page's viewmodel, and I want any controls within the user control to bind to properties of the user control, or of the user control's viewmodel.
I'd think this was a fairly common occurrence. But I've not been able to find any examples of how it might be done.
To restate the problem: I have a UserControl, that contains an embedded Telerik DataForm control. I need to bind the ItemsSource property of the embedded DataForm to a property of the DataContext of the page on which my UserControl is placed.
If the UserControl did not have its DataContext set, it would inherit the DataContext of the page, and I could easily bind the embedded DataForm's ItemsSource property to a property of it, but the UserControl has it's own DataContext.
If the UserControl was written to be used only on this page, I could bind the embedded DataForm's ItemsSource property to a property of the page, using RelativeSource binding. But this UserControl is intended to be used in a number of places, and cannot have silent dependencies on properties outside of the UserControl. I need to make the dependency explicit.
Creating a DependencyProperty on the UserControl is the right approach, but there's no need to try to replicate the property in the UserControl's viewmodel.
What I need to do is to
1: Add a DependencyProperty to the UserControl:
public QueryableCollectionView ItemsSource
{
get { return (QueryableCollectionView)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(QueryableCollectionView), typeof(FormControl));
Note: I do not need to implement a call-back function.
2: Bind the embedded DataForm's ItemsProperty to this new DependencyProperty of the UserControl, using RelativeSource binding:
<UserControl
...>
<telerik:RadDataForm
ItemsSource="{Binding Path=ItemsSource, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
/>
</UserControl>
3: Make the viewModel of the page visible, with the proper type (DataContext has type object):
public partial class MainWindow : Window
{
public MainWIndow()
{
this.InitializeComponent();
this.DataContext = new MainWindowVM();
}
public MainWindowVM viewModel
{ get { return this.DataContext as MainWindowVM; } }
}
4: On the page, bind the UserControl's new ItemsProperty DependencyProperty to the appropriate property of the page's viewmodel, again using RelativeSource binding:
<Window
...>
<local:FormControl
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type telerik:RadPane}},Path=viewModel.items}"
/>
</Window>

How can I bind from a user control to an external object in XAML?

I have an image inside a user control that I want to bind it's visibility to a property I have set up in a class object. The dependency properties are set up and working correctly, but I don't know how to set the binding properly on the image.
The user control and class object are in the same namespace. I thought I would need to set the ElementName to the window or the RelativeSource to the class object, but I'm not getting it to work out.
Here's what a dependency property looks like (defined in MigrateUserWizardObject.cs, this inherits from DependencyObject, this resides in the UserAccountMigrator namespace):
public static readonly DependencyProperty DatabaseStepCompletedVisibilityProperty = DependencyProperty.Register("DatabaseStepCompletedVisibility", typeof(Visibility), typeof(MigrateUserWizardObject));
public Visibility DatabaseStepCompletedVisibility
{
get
{
return (Visibility)GetValue(DatabaseStepCompletedVisibilityProperty);
}
set
{
SetValue(DatabaseStepCompletedVisibilityProperty, value);
}
}
Here's an image that I want bound to this dependency property (defined in ProgressUserControl.xaml, this inherits from UserControl, this resides in the UserAccountMigrator namespace as well):
<Image x:Name="DatabaseCompleted" Source="{StaticResource GreenCheckMarkSource}" Visibility="{Binding Path=DatabaseStepCompletedVisibility, UpdateSourceTrigger=PropertyChanged}" Height="20" HorizontalAlignment="Right"></Image>
This is due to the fact that the DataContext of the image is the user control. How can I make this work?
I think you should look into using the Model-View-ViewModel pattern. Instead of setting the DataContext to the UserControl, set it to an instance of another class (ProgressViewModel, for example). This view model would have all the properties you want to bind to (including your DatabaseStepCompletedVisibility property) and makes it much easier. Right now you are wanting to bind some things to the UserControl, some things to another object somewhere else, etc.. and, as you have found, makes it difficult. Here is more information:
http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/432/MVVM-for-Tarded-Folks-Like-Me-or-MVVM-and-What-it-Means-to-Me.aspx
Without going that approach, you have to have an instance MigrateUserWizardObject to bind to. You can put that instance in your UserControl (if you insist on using it as the DataContext), then you can bind the the property of the MigrateUserWizardObject property of the UserControl. Also, your MigrateUserWizardObject doesn't have to be a dependency object or dependency property to bind to. A better pattern would be to make it a plain c# class that implements the INotifyPropertyChanged interface.

Resources