MVVM - Add a Existing User Control in Grid(XAML) - c#

I am developing a Windows 8.1 apps,
and i am following MVVM Pattern
I have a Grid in the Application
<Grid Name="g1">
in which in need to add a existing User Control.
<UserControl
x:Class="CaptureApp.UIComponents.PlayVideo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CaptureApp.UIComponents"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid>
<MediaElement Name="MediaPlay" >
</MediaElement>
</Grid>
</UserControl>
Since View (XAML) is not allowed to know the Control.
What will be the correct way to implement it??

the wordpress blog in the comments uses a datatrigger, which isn't present in windows store apps.
if I understand your question correctly, you're trying to have a view within your grid that is conditionally loaded, so that when there is no data for the user control, it is not rendered in the grid?
you could accomplish this by using a
<ContentControl Content="{Binding PropertyOnViewModel}" ContentTemplateSelector="{StaticResource SomeContentTemplateSelector}" />.
public class SomeContentTemplateSelector : DataTemplateSelector
{
public DataTemplate SomeTemplate {get;set;}
protected override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is null)
return null;
return SomeTemplate;
}
}
and then in a DataTemplate, have your UserControl as a child. This will display nothing when there is no Content bound to the ContentControl, and will otherwise display the supplied DataTemplate. You will need to have a property in the over-arching ViewModel that contains the content for this ContentControl, though, just fyi.
edit: if you're adding multiple items dynamically, then you will want an ObservableCollection<> property on your ViewModel, and use an ItemsControl instead of a ContentControl.

Related

WPF usercontrol mvvm

I have a problem implementing MVVM with a usercontrols.
I have an MVVM based application.
In one of the view (which is a usercontrol) I have a menu on the left and content on the right. The content change depending on the menu.
I tried to implement the MVVM with a usercontrol, but i dont know how.
Here is what i tried but it didn't work :
<UserControl x:Class="PoS.Views.OptionsView"
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:local="clr-namespace:PoS.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<DataTemplate x:Name="SettingsTemplate" DataType="{x:Type viewmodels:SettingsViewModel}">
<views:SettingsView DataContext="{Binding}" />
</DataTemplate>
</UserControl.Resources>
<Grid>
</Grid>
</UserControl>
I'll be honest, I think you need to rewind a bit and read a good book on MVVM before continuing. Gary McLean Hall's Pro WPF and Silverlight MVVM is a good place to start.
To answer your question, I'll assume that this user control is set up with its DataContext pointing to your MainViewModel. The content on the right needs a corresponding property in the main view model i.e. something like this:
private ViewModelBase _CurrentPage;
public ViewModelBase CurrentPage
{
get { return this._CurrentPage; }
set
{
if (this._CurrentPage != value)
{
this._CurrentPage = value;
RaisePropertyChanged(() => this.CurrentPage);
}
}
}
You then create a bunch of "pages" or something that inherit ViewModelBase i.e. Page1ViewModel, Page2ViewModel, SettingsViewModel etc. You then create a ContentControl and bind its content to that property:
<ContentControl Content="{Binding CurrentPage}" />
So now if your view model does something like CurrentPage = new SettingsViewModel() then the ContentControl will be populated with whatever you declared as the DataTemplate for that type (i.e. a control of type views:SettingsView). If you assign the property to something else then the SettingsView will be destroyed and replaced by whatever the DataTemplate for the new type is.
In your example above only SettingsViewModel/SettingsView will work, because that's all you've created a DataTemplate for; in order for this to work you need to create a separate DataTemplate for each ViewModel/View pair type you create.

WPF user control inheritance issues

I made a WPF control in a library project and would like to extend it with a new one.
<UserControl x:Class="Genesyslab.Desktop.Modules.ExtensionUtils85.GUI.EmbeddingUserControl"
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:DesignHeight="300" d:DesignWidth="300">
<Grid>
</Grid>
</UserControl>
I have tried to extend it like this:
<src:EmbeddingUserControl x:Class="Interaxalab.Desktop.Modules.PrototipoCable.CustomViews.InteractionView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="Genesyslab.Desktop.Modules.ExtensionUtils85.GUI"
Name="InteractionWorksheetView" Height="321.613" Width="471.396"
>
<Grid>
<WindowsFormsHost x:Name="windowsFormsHost1" HorizontalAlignment="Left" Height="284" VerticalAlignment="Top" Width="471"/>
</Grid>
</src:EmbeddingUserControl>
However, I get an error message saying that the name "EmbeddingUserControl" does not exist in namespace "Genesyslab.Desktop.Modules.ExtensionUtils85.GUI".
The name clearly does exist, since the xaml.cs can find it, but for some reason the xaml cannot.
Any help would be appreciated.
Long story short - you cannot inherit control with xaml by another control with xaml (and does it makes sense even to do so?). In your case, EmbeddingUserControl does not contain any visual tree elements (just empty grid), so you can just do:
public class EmbeddingUserControl : UserControl {
// some methods, properties of your control
}
Then you can inherit exactly like you do already in your question (don't forget to inherit from EmbeddingUserControl both in xaml file and in code-behind file).
You can also inherit from user control with xaml, if your inherited control does not have xaml itself (so you can add\override logic).
If you need to inherit some visual stuctures - you have to switch from inheritance to composition. That is: your base control provides some placeholders where other controls may be placed, or even better allows to provide templates to format data items (like for example ItemsControl and ItemTemplate property). Then you just fill those placeholders with other controls if necessary.

How can I bind other components besides ContentControl to a view in Caliburn Micro?

So in Caliburn Micro, I have been using the following method to compose a view inside of another view:
Put a ContentControl inside the composing View.
Create a property on the composing ViewModel, and assign to it the composed ViewModel
Give the ContentControl a x:Name attribute that matches the name of the composed ViewModel property on the composing ViewModel.
like so...
View:
<UserControl x:Class="MyProject.MyComposingView"
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">
<ContentControl x:Name="MyComposedViewModel"/>
</UserControl>
ViewModel:
class ComposingViewModel : PropertyChangedBase
{
private ComposedViewModel _myComposedViewModel;
public ComposedViewModel MyComposedViewModel
{
get { return _myComposedViewModel; }
set
{
_myComposedViewModel= value;
NotifyOfPropertyChange(() => Page);
}
}
public ComposingViewModel(ComposedViewModel myComposedViewModel)
{
MyComposedViewModel = myComposedViewModel;
}
}
Caliburn Micro automagically figures out that because it's a ContentControl it obviously doesn't want to bind to a ViewModel, but rather to its associated View, and so it does something under the hood to bind the ContentControl's Content property to MyComposedView instead of MyComposedViewModel.
But, what if I don't want to use a ContentControl? Like, maybe some reusable custom component of mine that wraps a ContentControl instead? For example:
<UserControl x:Class="MyProject.MyContentWrapper"
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">
<Grid x:Name="PreviewBox" SizeChanged="onSizeChanged">
<Image x:Name="BGImage" Source="{Binding BGImage}"/>
<ContentControl Content="{Binding}"/>
</Grid>
</UserControl>
If I replace the ContentControl with a MyContentWrapper, CaliburnMicro no longer works its magic to supply MyComposedView, and I end up with a TextBlock that says, MyProject.MyComposedViewModel.
How can I get CaliburnMicro to know this is a situation where it should supply the View rather than the ViewModel?
What you want to do is add a convention for your custom control:
Go to the code for ConventionMananger on github.
Search for AddElementConvention<ContentControl>.
Create a new method in your Bootstrapper that runs when your application starts. Add a call to ConventionManager.AddElementConvention<YourControl> similar to the one for ContentControl.
Make sure to put a ContentPropertyAttribute on your control and specify the content property.
Disclaimer: I'm on mobile and can't validate this.

How can I find an item with specific value, in a Collection bound to a Canvas

I've been Google'ing a bit around for this, but without getting any real answer. Probably because my question might be a bit cryptic. Here goes:
Lets say i have an ObservableCollection<SomeModel> containing a bunch of models. I then add the corresponding Views to a Canvas. Specifying this in the resources of the Window, and then bind the Canvas' ItemsSource to the ObservableCollection<SomeModel>. This works fine.
SomeModel is bound to SomeView, this is a UserControl.
Now, when this View gets focus, or when I MouseDown on it, I would like to have it marked as "Selected". Somehow, i would like to have a property in the codebehind to the Window holding my Canvas, where i can always get the selected item.
I've been thinking of having a BindingList instead of the ObservableCollection, and when an IsSelected property on the model changes, then a method will extract the selected item from the list. But this seems to be a bit of a performance killer, as i will be notified on all changes to the items.
How can I accomplish this?
There are multiple ways you could solve this. But probably the simplest it to work with a ListBox and bind to it. ListBox as it has bindable ItemsSource and a SelectedItem property that has the item that is currently selected in it. It also calls the SelectionChanged event when the selection changes if you want to do something in the code behind .cs file.
I would recommend keeping the ObservableCollection in your View Model to keep true to MVVM.
If the style or placement of ListBox does not suit your override the template to something that suits your needs better.
You could look at a behaviour if the above doesn't work for you but best to keep it simple.
UPDATE
This is how you would build the view. Note the ItemsPanel attribute is bound to a defined ItemsPanelTemplate in the UserControl.Resources section which specifies a Canvas to put the items on.
<UserControl
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"
mc:Ignorable="d"
xmlns:local="clr-namespace:SilverlightApplication1"
x:Class="SilverlightApplication1.View1"
d:DesignWidth="640" d:DesignHeight="480">
<UserControl.Resources>
<local:View1Model x:Key="View1ModelDataSource" />
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<Canvas />
</ItemsPanelTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource View1ModelDataSource}}">
<ListBox Margin="80,85,183,54" ItemsPanel="{StaticResource ItemsPanelTemplate1}" ItemsSource="{Binding DataModelCollection}"/>
</Grid>
On the View Model
public class View1Model
{
private ObservableCollection<SomeModel> _DataModelCollection;
public ObservableCollection<SomeModel> DataModelCollection
{
get { return this._DataModelCollection; }
set { this._DataModelCollection = value; }
}
}
It should be noted though that Canvas itself does not have any logic to let the user move controls around on it at runtime.

C# User Control that can contain other Controls (when using it)

I found something about this issue for ASP, but it didn't help me much ...
What I'd like to do is the following: I want to create a user control that has a collection as property and buttons to navigate through this collection. I want to be able to bind this user control to a collection and display different controls on it (containing data from that collection).
Like what you had in MS Access on the lower edge of a form ...
to be more precise:
When I actually use the control in my application (after I created it), I want to be able to add multiple controls to it (textboxes, labels etc) between the <myControly> and </mycontrol>
If I do that now, the controls on my user control disappear.
Here is an example of one way to do what you want:
First, the code - UserControl1.xaml.cs
public partial class UserControl1 : UserControl
{
public static readonly DependencyProperty MyContentProperty =
DependencyProperty.Register("MyContent", typeof(object), typeof(UserControl1));
public UserControl1()
{
InitializeComponent();
}
public object MyContent
{
get { return GetValue(MyContentProperty); }
set { SetValue(MyContentProperty, value); }
}
}
And the user control's XAML - UserControl1.xaml
<UserControl x:Class="InCtrl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Name="MyCtrl">
<StackPanel>
<Button Content="Up"/>
<ContentPresenter Content="{Binding ElementName=MyCtrl, Path=MyContent}"/>
<Button Content="Down"/>
</StackPanel>
</UserControl>
And finally, the xaml to use our wonderful new control:
<Window x:Class="InCtrl.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:InCtrl"
Title="Window1" Height="300" Width="300">
<Grid>
<me:UserControl1>
<me:UserControl1.MyContent>
<Button Content="Middle"/>
</me:UserControl1.MyContent>
</me:UserControl1>
</Grid>
</Window>
I'm having a hard time understanding your question, but I think what you're describing is an ItemsControl using DataTemplates to display the contents of (presumably) an ObservableCollection(T).
A UserControl may not be the best way to do this. You're wanting to add decorations around content, which is basically what Border does: it has a child element, and it adds its own stuff around the edges.
Look into the Decorator class, which Border descends from. If you make your own Border descendant, you should be easily able to do what you want. However, I believe this would require writing code, not XAML.
You might still want to make a UserControl to wrap the buttons at the bottom, just so you can use the visual designer for part of the process. But Decorator would be a good way to glue the pieces together and allow for user-definable content.
Here's a link to a built-in control (HeaderedContentControl) that does the same thing as the accepted answer except that it is an existing control in WPF since .Net 3.0

Categories

Resources