Custom Control with Resource Dictionary - wpf

I have created a custom control for which I would like to be able to add resources. Right now I am enclosing the custom control in a ContentControl as below. Would it be possible to change MyControl such that I can have a ResourceDictionary inside of MyControl? Maybe like an attached DependencyProperty?
<ContentControl>
<ContentControl.Resources>
...
</ContentControl.Resources>
<utils:MyControl
Propery_A="{Binding Some_A}"
Propery_B="{Binding Some_B}"
DeleteButtonContent="{StaticResource customDeleteButtonContent}"
/>
</ContentControl>

Would it be possible to change MyControl such that I can have a ResourceDictionary inside of MyControl?
I may have misunderstood your issue but you could just inherit from FrameworkElement or any of its descendants, like for example Control:
public class MyControl : Control
...
Then you can set its Resources property like you set it for any other control:
<utils:MyControl
Propery_A="{Binding Some_A}"
Propery_B="{Binding Some_B}"
DeleteButtonContent="{StaticResource customDeleteButtonContent}"
<utils.MyControl.Resources>
...
</utils.MyControl.Resources>
</utils:MyControl>
No need to wrap your control in a ContentControl.

Related

Showing UserControl once at the time with DataTemplateSelector

I have a couple specific user controls to Show some Content, e.g. simple like Image, WebControl but also two complex specific custom controls drawing on a canvas.
Now I thought using the DataTemplateSelector to handle the different UserControls. I actully used this http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector as a reference.
I changed the code so the form loads the UserControls dynamically (according to the file extension) in the following collection:
ObservableCollection<string> _pathCollection = new ObservableCollection<string>();
The only difference to the reference is now I want to navigate back and forward to the next control by showing one control only at the time. Which control should I use instead of ListView?
<Grid>
<ListView ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding ElementName=This, Path=PathCollection}"
ItemTemplateSelector="{StaticResource imgStringTemplateSelector}">
</ListView>
</Grid>
How do I need to bind it to the template (equal to ItemTemplateSelector above)? WPF is still very new to me and I am learning.
Use a ContentControl. Bind your current item to the Content-property and the DataTemplateSelector to the ContentTemplateSelector-property.
<ContentControl Content="{Binding Path=CurrentItem, Mode=OneWay}", ContentTemplateSelector="{StaticResource imgStringTemplateSelector}" />
Your CurrentItem should be a DependencyProperty or a INotifyPropertyChanged-property of your DataContext. When you change your CurrentItem, the ContentControl will update the template automatically with help of your TemplateSelector.

how to load wpf usercontrol in MVVM pattern

I'm creating a wpf user control which is in mvvm pattern.
So we have : view(with no code in codebehind file), viewmodel,model,dataaccess files.
I have MainWindow.xaml as a view file, which I need to bind with MainWindowModel.cs.
Usually, in a a wpf application we can do this with onStartUp event in App.xaml file. But in user control, as we do not have App.xaml...How do I achieve it ?
Please help :(...Thanks in Advance !!!
You can use a ContentControl, with a DataTemplate to bind the UserControl (View) to the ViewModel :
<DataTemplate DataType="{x:Type vm:MyViewModel}">
<v:MyUserControl />
</DataTemplate>
...
<ContentControl Content="{Binding Current}" />
WPF will pick the DataTemplate automatically based on the type of the Content
I know this is an old, answered question, but I have a different approach. I like to make implicit relationships in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
With this, there is no need to set a DataContext anywhere.
UPDATE >>>
In response to #Vignesh Natraj's request, here is a fuller explanation:
Once you have set up the DataTemplate in a Resources element, you can display the KioskView in this example by adding an instance of the KioskViewModel anywhere in your XAML. This could be filling the MainWindow, or just inside a particular section of the screen. You could also host multiple instances of the KioskViewModel in a ListBox and it will generate multiple KioskView instances.
You can add an instance of the KioskViewModel to your XAML in a couple of ways, depending on your requirements. One way is to declare the XML namespace for the project that contains the KioskViewModel.cs file and simply add an instance of it in a ContentControl to the page where you want your view to appear. For example, if you had a UserControl called MainView and the KioskViewModel.cs file was in a Kiosk.ViewModels namespace, you could use basic XAML like this:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<UserControl.Resources>
<ViewModels:KioskViewModel x:Key="KioskViewModel" />
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</UserControl.Resources>
<ContentControl Content="{StaticResource KioskViewModel}" />
</UserControl>
I prefer to use the MVVM design pattern with WPF, so I would have a base view model class providing useful functionality such as implementing the essential INotifyPropertyChanged interface. I then have a property called ViewModel in the main (top level) view model of type BaseViewModel. This provides me with a nice way to change the ViewModel property to any view model that has derived from BaseViewModel and therefore to be able to change the associated view from the view model.
For example, in the MainViewModel.cs class that is bound to MainView there is a field and relating property:
private BaseViewModel viewModel = new KioskViewModel();
public BaseViewModel ViewModel
{
get { return viewModel; }
set { viewModel = value; NotifyPropertyChanged("ViewModel"); }
}
As you can see, it starts off as a KioskViewModel instance, but can be changed to any other view at any time in response to user interaction. For this setup, the XAML is very similar, but instead of declaring an instance of the view model in the Resources element, we bind to the property in the MainViewModel:
<UserControl x:Class="Kiosk.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:Kiosk.ViewModels">
<ContentControl Content="{Binding ViewModel}" />
</UserControl>
Note that for this example, we would need to declare two (or more to make this approach useful) DataTemplates in the App.xaml file:
<Application.Resources>
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModels:KioskViewModel}">
<Views:KioskView />
</DataTemplate>
</Application.Resources>
I've been using MVVM Light Toolkit which has a ViewModelLocator class that you can put properties to the viewmodels in. You then create a reference to the ViewModelLocator in your Mainwindow.xaml like so:
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True"/>
In the grid panel, or whatever you're using, you can then set the datacontext like this:
<Grid DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
...
</Grid>
You could also go with MEFedMVVM which potentially adds a bit more flexibility in terms of being able to swap different viewModel implementations into the view.
The flexibility in both of these libraries is that you don't have to use their ViewModel base classes if you don't want to - the ViewModelLocator and the MEFedMVVM can work with any class.
There are endless ways to do it, wich all fall in one of the two categories:"view first" or "model first".
In a "view first" mode the view (e.g. your mainwindow) is created first and then (e.g. in the codebehind) the View instantiates the ViewModel and sets it as its datacontext):
private void WindowLoaded(object sender, EventArgs args)
{
this.DataContext = ViewModelService.GetViewModelX();
}
In a "model first" mode the ViewModel is there first and then instanciated the View.
// method of the viewmodel
public void LoadView()
{
// in this example the view abstracted using an interface
this.View = ViewService.GetViewX();
this.View.SetDataContext(this);
this.View.Show();
}
The examples given here are just one way of many. You could look at the various MVVM frameworks and see how they do it.
We can use ObjectDataProvider to call a method inside an object ..as follows :
<ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
MethodName="ConvertTemp"
x:Key="convertTemp">
Is there anyway to do the same using DataTemplate
You can probably look at MSDN. I find it as a good resource, though it doesn't explain how to use usercontrols,you will find your way out.

Custom TabItem in TabControl

I've created CustomTabItem which inherits from TabItem and i'd like to use it while binding ObservableCollection in TabControl
<TabControl ItemsSource="{Binding MyObservableCollection}"/>
It should like this in XAML, but i do not know how change default type of the output item created by TabControl while binding.
I've tried to create converter, but it has to do something like this inside convertin method:
List<CustomTabItem> resultList = new List<CustomTabItem>();
And iterate through my input ObservableCollection, create my CustomTab based on item from collection and add it to resultList...
I'd like to avoid it, bacause while creating CustomTabItem i'm creating complex View and it takes a while, so i don't want to create it always when something change in binded collection.
My class extends typical TabItem and i'd like to use this class in TabControl instead of TabItem.
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type local:CustomTabItem}">
<Setter Property="MyProperty" Value="{Binding xxx}"/>
</Style>
</TabControl.ItemContainerStyle>
Code above generates error that Style cannot be applied to TabItem.
My main purpose is to use in XAML my own CustomTabItem and bind properties... Just like above...
I've also tried to use
<TabControl.ItemTemplate/>
<TabControl.ContentTemaplte/>
But they are just styles for TabItem, so i'll still be missing my properties wchich i added in my custom class.
You will need to create a custom class derived from TabControl and override GetItemForContainerOverride to return your custom TabItem:
protected override DependencyObject GetContainerForItemOverride()
{
return new CustomTabItem();
}

User defined top level control in XAML

A normal UserControl looks like this in XAML:
<UserControl x:Class="mynamespace.foo" ...namespaces...>
<!-- content -->
</UserControl>
I'd like to be able to define my own top level object, along the lines of:
<MyControl x:Class="mynamespace.mycontrol" ...namespaces...>
<!-- content -->
</UserControl>
Where MyControl derives from a UserControl itself.
Of course the compiler complains about "MyControl" not being found. Is there a way around this?
The root tag is the base class. That's why the root of the default Window1 is Window. Using the menu option Add > UserContol... is in fact creating a sub-class for UserContol.
If you have some common elements and want a control base class you can use the base class as the root tag. You can't derive your class from any class that has a xaml defined visual tree, but your base class can derive from UserConrtol.
First define your base class:
public class MyControlBase : UserControl
{
// ...
}
Then create your specific child class:
(You can start with the automatically created UserControl1 and change it from there)
public partial class MyControl1 : MyControlBase
{
public MyControl1()
{
InitializeComponent();
}
}
Then change the Xaml side to look like this:
<MyNamespace:MyControlBase
x:Class="MyNamespace.MyControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:MyNamespace="clr-namespace:MyNamespace">
This is a great way to make custom controls derived from built in ones other that UserControl. It is typically recommended to just use basic UserConrtols if you can and make a custom control only if you have to.
good luck,
Define your namespace in the XAML and then use your control name as the tag:
<Window ...
xmlns:my="..." />
<my:mycontrol ... />
</Window>
No. The XAML is declaring what a MyControl visually is, just as the code-behind is defining what a MyControl behaviourally is. Defining the visuals of a MyControl in terms of a MyControl wouldn't really make sense: it's the equivalent of, in the code-behind, deriving MyControl from MyControl, which you obviously wouldn't do.
In addition, WPF doesn't let you derive one UserControl class from another e.g. if BobsControl is a UserControl then you can't write <local:BobsControl x:Class="MyNamespace.MyControl... /> either. I believe this is because UserControls have a visual appearance (content) baked into their XAML and the content of the derived class would have to replace the content of the base class, so the visual inheritance is generally not useful.
However, you can do it if the top-level element you're deriving from is a custom control. Custom controls are lookless (not defined in XAML). So you can create your own top-level element as a custom control, and then derive "user" controls from that. (If you do go down this route, you'll probably want to derive your custom control from ContentControl or apply ContentPropertyAttribute, so that your top-level element can easily contain other XAML.)

Binding to an element within a UserControl

Say I have a user control like the one below, how would I bind something to the ActualWidth of the "G1" grid from outside of the control?
<UserControl x:Class="Blah">
<WrapPanel>
<Grid x:Name="G1">
...
</Grid>
<Grid>
...
</Grid>
</WrapPanel>
</UserControl>
If you mean with outside the control, not as Content of the control, you can use ElementName in the Binding like so:
{Binding ElementName=G1, Path=ActualWidth}
If you mean outside the control in another Xaml file, then you can try to use the Path property if your control is in the scope of the other control:
{Binding ElementName=ParentControl, Path=G1.ActualWidth}
However I would advise against this design, because you may change the name of G1 one day, and you would never know of any bindings that might break.
If you want to bind to an external control where you use this user control, declare a DependencyProperty at your UserControl code behind and then Bind G1 to that property.
And bind the external control's property to the UserControl's DependencyProperty.
It is like a 2 level of indirection.

Resources