Silverlight: Nested containers and Binding - silverlight

The Scenario Bit:
On one of the controls within my Silverlight application I have embedded a custom user control. Within this embedded control is another custom user control that contains a datagrid. I would like to use binding to populate the datagrid. Easy enough I just sepcificy a collection that is in the DataContext of the parent control.
Parent Form:
<UserControl x:Class="ParentControl"
...>
<Grid x:Name="LayoutRoot" >
<ReusableControl />
</Grid>
</UserControl>
Parent Codebehind:
public partial class ParentControl: UserControl
{
public ParentControl()
{
InitializeComponent();
this.DataContext = ObjectCollection;
}
public ObservableCollection<object> ObjectCollection
{
get ;
set ;
}
}
Intermediate Form
<UserControl x:Class="ReusableControl"
...>
<Grid x:Name="LayoutRoot" Background="Gold">
<CustomDataGrid />
</Grid>
</UserControl>
Child Control:
<UserControl x:Class="CustomDataGrid"
...>
<Grid x:Name="LayoutRoot">
<data:DataGrid x:Name="dgItems"
AutoGenerateColumns="True"
ItemsSource="{Binding ObjectCollection}"
>
</data:DataGrid>
</Grid>
</UserControl>
The Question Bit:
I want to specificy the columns of the datagrid dynamically, based on another collection in the parent control DataContext. How can I do this? Is there more than one way of skinning this cat?*
Thanks,
Mark
*No cats where harmed during the asking of this question.

After many hours I have found a work-around which I have posted here. This doesn't strike me as the best solution in the world, but it works, and doesn't need the registration of event handlers throughout the application. Also it works top down, which is what I wanted.
I suspect that I could use Dependency Properties a little better, to prevent the need for DP's and NP's in the same class, but I'm out of time :-(
Hope this helps someone else.

Related

WPF: How to create events for custom usercontrols?

I have created a UserControl that consists of an ItemsCollection. Each item in this collection consists of a ListBox.
My application is represented by a Window, which contains this UserControl. I want to be able to manage events related to items inside the ListBox. How can I achieve this? (I am not sure if this is relevant or not, but the UserControl is in a assembly different from the application.)
Here's the code of the UserControl:
<UserControl
x:Class="UserControls.CalendarMonthViewControl.CalendarMonthView"
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:DesignWidth="580"
d:DesignHeight="300"
xmlns:calendarMonthViewControl="clr-namespace:UserControls.CalendarMonthViewControl"
Name="CalendarMonthViewControl">
<Grid>
<ItemsControl
ItemsSource="{Binding ElementName=CalendarMonthViewControl, Path=Days}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="6" Columns="7" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type calendarMonthViewControl:Day}">
<Border>
<Grid>
<ListBox ItemsSource="{Binding Path=CalendarDayItems}" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
#vlad is on the right track.
There's a couple of options here. Both of them involve handling routed events.
To handle a routed event, you use the name of the owning class followed by the name of the event.
The first option is to simply handle the selection changed events (or some other ListBox event) on the Window class:
<Window ...
ListBox.SelectionChanged="OnChildListboxSelectionChanged">
...
</Window>
The second option (more typical approach) is to handle the ListBox events inside of the UserControl and then aggregate them in some way and fire an event at that level. This event is then handled by the Window. This event could be a routed event or a standard .NET event.
<UserControl ...
ListBox.SelectionChanged="OnChildListBoxSelectionChanged">
...
</UserControl>
Code behind for user control:
public event EventHandler<MyArgTypeEventArgs> ListBoxUpdated;
private void OnChildListBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
// aggregate data and info
MyArgTypeEventArgs handler = ListBoxUpdated;
if (handler != null)
handler.Invoke(this, GenerateArgs());
}
The Window handles this event:
<Window ...
ListBoxUpdated="OnListBoxUpdated">
...
</Window>
This should give you something to start with.
I haven't used them much myself, but I think RoutedEvents would solve your problem. The events bubble up from your ListBox to the Window (or another element lower in the tree) where you can handle them.
edit: quoting from the link: To add a handler for an event using XAML, you declare the event name as an attribute on the element that is an event listener. The value of the attribute is the name of your implemented handler method, which must exist in the partial class of the code-behind file.
since UserControl inherits from UIElement, I'm guessing something like this would work (untested atm):
<UserControl
x:Class="UserControls.CalendarMonthViewControl.CalendarMonthView"
[...]
ListBox.NameOfEvent="EventHandlerName">
I don't think what you're trying to do is the right approach as it's creating unnecessary dependence between your views and means that your UserControl is not properly encapsulated.
In my opinion the nice solution would be to handle events in your UserControl's viewmodel and set up a relation between the viewmodel of your Window as needed, so that the views themselves are independent.

MVVM way to use different controls for editing different objects

I need to design a form with a treeview in the left and a container for some other control in the remaining area. Whenever user selects an item in the treeview some custom control appears on the right. For example, let's say that the treeview contains values "Audio settings" and "Video settings", and I have two controls that can be bound to these settings and I want to display them on the form when needed.
Now, from what I've read about MVVM, I shouldn't have properties that will return UserControls or DataTemplates, am I right? It will be messing with "VM shouldn't know implementation details of the view" as I see it. So, how do I handle this situation properly in terms of MVVM? Should I maybe use converters for this and if so, how would it look?
I can't provide any code at the moment (mostly because there isn't any), but I will try to clarify the problem if needed.
Thanks in advance.
This is where the WPF templating system helps out.
The main idea is to have a ContentControl display the appropriate view depending on the selected value in the TreeView.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<ListBox DockPanel.Dock="Left" ItemsSource="{Binding Editors}" SelectedItem="{Binding SelectedEditor}" />
<ContentControl Content="{Binding SelectedEditor}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type l:VideoViewModel}">
<l:VideoView />
</DataTemplate>
<DataTemplate DataType="{x:Type l:AudioViewModel}">
<l:AudioView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</Window>
AudioView and VideoView are UserControls.
As you can see, the ContentControl's content is bound to the SelectedEditor property in the ViewModel, which is also bound to the SelectedItem of the Listbox.
So the ViewModel for the main view looks like this.
public class MainWindowViewModel : INotifyPropertyChanged
{
public IEnumerable<object> Editors
{
get
{
yield return new VideoViewModel();
yield return new AudioViewModel();
}
}
private object selectedEditor;
public object SelectedEditor
{
get { return selectedEditor; }
set
{
if (selectedEditor == value)
return;
selectedEditor = value;
OnPropertyChanged("SelectedEditor");
}
}
}
So you can see that the main ViewModel has no GUI data in it.
To handle hooking up a TreeView to a SelectedItem property in a ViewModel see Data binding to SelectedItem in a WPF Treeview
You can implement it throw two properties: ShowAudioSettings and ShowVideoSettings in ViewModel and bind it on Visibility of your controls.
Also, you can make it with VisualStateManager.

MVVM WPF design related query : use of UserControls

I have one query related to designing WPF using MVVM
Here is the scenario :
1> I have one WPF screen which contains various user controls which are reusable in some other screens too.
2> Can i have separate ViewModel class for each of those user controls , what could be ideal design in this scenario
3> Should i separate my Viewmodel based on individual screen or on UserControls .
4> If i create separate viewmodels based on UserControls how i should integrate it .
Is there any design guidelines around this !!
Urgent Help appreciated ..
This post describes what I do in certain scenario, I don't know if it is a best practice or not but it works for me.
I create ViewModel for my Window that holds all the user controls, so this called ContainerViewModel and I create an instance of that Viewmodel and put it in the DataContext of the Window. From that moment all the UserControls can access that ViewModel with Binding.
The next thing to do is to create a property on my ContainerViewModel for everty UserControl that holds the ViewModel for each UserControl.
Then use binding to attach the usercontrols ViewModel to the DataContext property of the Usercontrol.
example of the viewmodels and a window with 2 listboxes instead of usercontrols:
Viewmodel classes without any implementation but just empty classes to show the concept:
public class ContainerViewModel
{
public ContainerViewModel()
{
ViewModelForControl1 = new Control1ViewModel();
ViewModelForControl2 = new Control2ViewModel();
}
public Control1ViewModel ViewModelForControl1 { get; set; }
public Control2ViewModel ViewModelForControl2 { get; set; }
}
public class Control1ViewModel { }
public class Control2ViewModel { }
Window xaml:
<Window x:Class="ConfigHellp.UI.Windows.ContainerWindow"
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:vm="clr-namespace:ConfigHellp.UI.ViewModel"
mc:Ignorable="d"
DataContext="{DynamicResource ContainerViewModel}" >
<Window.Resources>
<vm:ContainerViewModel x:Key="ContainerViewModel" d:IsDataSource="True" />
</Window.Resources>
<StackPanel>
<ListBox DataContext="{Binding ViewModelForControl1}" />
<ListBox DataContext="{Binding ViewModelForControl2}" />
</StackPanel>
</Window>
this depends on how complex the embedding of the UserControl into the environment is. If you think that its to much effort to build the view model logic for your user control again and again (which is also a very nice source for mistakes), you should infact encapsulate the logic in a single viewmodel for your control. If the user control will be an ListItem for example, i generally suggest you to build an own viewmodel for the control.
The infrastructure will be than:
A general viewmodel for your WPF screen, which holds instances of the viewmodels for your usercontrols. That DataContext of the screen will be the general viewmodel. The users controls's DataContext will be a Binding to the PropertyPath of the user control viewmodel in your general viewmodel. e.g:
In WPF Screen:
<ListBox DataContext="{Binding}" ItemsSource="{Binding Path=ItemList}">
<ListBox.ItemTemplate>
<yourControls:YourUserControl />
</ListBox.ItemTemplate>
</ListBox>
In the general viewmodel:
public class ScreenViewModel : INotifyPropertyChanged
{
private ObservableCollection<YourUserControlViewModel> _itemList =
new ObservableCollection<YourUserControlViewModel>();
public ObservableCollection<YourUserControlViewModel> ItemList
{
get { return _itemList; }
set { _itemList = value; }
}
}
This will automatically generate a your user control for each viewmodel in the ItemList of your general view model.

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.

Why would ItemsControl show a view but ContentControl show nothing?

I've got an application based on Prism.
This is my shell:
<Window x:Class="AvarioCRM3.ShellV2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.codeplex.com/CompositeWPF" >
<DockPanel LastChildFill="True">
<Border
Padding="10"
DockPanel.Dock="Top"
Background="#ddd">
<DockPanel>
<ItemsControl
Name="MainNavigationPanel"
cal:RegionManager.RegionName="MainNavigationPanel"
DockPanel.Dock="Top"/>
</DockPanel>
</Border>
</DockPanel>
</Window>
In my MenuModule I add a view to the region and it shows fine:
public void Initialize()
{
MainNavigationPresenter mainNavigationPresenter = this.container.Resolve<MainNavigationPresenter>();
IRegion mainRegion = this.regionManager.Regions["MainNavigationPanel"];
mainRegion.Add(new TestView());
}
The problem is: I don't want an ItemsControl in my shell, I want a ContentControl, but when I use a ContentControl, it shows nothing.
Why would ItemsControl show my views and ContentControl show nothing?
Could this be because a ContentControl will only display a single child, whereas an ItemsControl has multiple children?
I have't worked with Prism, but the API suggests that an IRegion is expected to have multiple children. If you're using a ContentControl then it is a little ambiguous what happens when I do the following:
IRegion mainRegion = this.regionManager.Regions["MainNavigationPanel"];
mainRegion.Add(new TestView());
mainRegion.Add(new SecondTestView());
Unlike the ItemsControl with a ContentControl you also need to activate the view once you have added it to make it visible.
MainNavigationPresenter mainNavigationPresenter = this.container.Resolve<MainNavigationPresenter>();
IRegion mainRegion = this.regionManager.Regions["MainNavigationPanel"];
TestView view = new TestView()
mainRegion.Add(view);
mainRegion.Activate(view);
I noticed you are doing this in Initialize. Could be too early? Have you tried using registration rather than injection of your view to see if that changed anything?
regionManager.RegisterViewWithRegion("MainNavigationPanel", typeof(TestView));
This won't solve your problem, however it will prove that the problem is trying to add something before your region is actually available. RegisterViewWithRegion will delay the creation and display of the view until the region is available.

Resources