new to .NET ecosystem.
The following code is working fine following MVVM in a blank UWP app.
<Page
x:Class="UWP.Blank.Mvvm.App01.Views.PrincipalPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UWP.Blank.Mvvm.App01"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:md="using:UWP.Blank.Mvvm.App01.Models"
xmlns:vm="using:UWP.Blank.Mvvm.App01.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
x:Name="ThisPrincipalPage">
<Page.Resources>
<DataTemplate x:Name="FlipView_ItemTemplate" x:DataType="md:MyFlipViewItem">
<Grid>
<Image Source="{x:Bind ImageLocation}" Stretch="Uniform" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="AdaptativeGridView_ItemTemplate">
<Grid
Background="White"
BorderBrush="Black"
BorderThickness="1">
<FlipView Width="300" Height="300"
ItemTemplate="{StaticResource FlipView_ItemTemplate}"
ItemsSource="{Binding ElementName=ThisPrincipalPage, Path=ViewModel.FlipViewData}"/>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid>
<controls:AdaptiveGridView x:Name="AdapGridView1"
ItemsSource="{x:Bind Path=ViewModel.AdaptativeGridViewData}"
ItemTemplate="{StaticResource AdaptativeGridView_ItemTemplate}"/>
</Grid>
</Page>
Which looks like:
enter image description here
The code behind is:
public sealed partial class PrincipalPage : Page
{
// Reference to our view model.
public PrincipalViewModel ViewModel { get; set; }
public PrincipalPage()
{
this.InitializeComponent();
this.ViewModel = new PrincipalViewModel();
}
}
And the ViewModel is an instance of this class:
public class PrincipalViewModel
{
public ObservableCollection<int> AdaptativeGridViewData { get; set; } = new ObservableCollection<int>() { 1, 2, 3, 4 };
public ObservableCollection<MyFlipViewItem> FlipViewData { get; set; } = new ObservableCollection<MyFlipViewItem>();
...
}
that provides two lists, one for the AdaptativeGridView control, and another for all the FlipViews inside of it.
My question is, is there any way I can use x:Bind only? Basically, because it claims to be faster. In case it is not possible, x:Bind is not a substitute of Binding, and makes everything even more confusing.
I tried two basic "intuitive options" which don't work.
Option 1, where ItemsSource property of the FlipView is changed to use x:Bind. Error claims:
Invalid binding path 'ViewModel.FlipViewData' : Property 'ViewModel' not found on type 'DataTemplate'.
Seems FlipView's DataTemplate has a DataContext different than expected. Maybe the DataContext is pointing to ViewModel.AdaptativeGridViewData, but, who knows? It is XAML. This is one of the reasons I am finding XAML an inconvenient since it behaves like a "black box". You know how to do something or you are in trouble.
In this case I tried to use classic Binding with "FindAncestor" utility to point back to ViewModel. In UWP however, AncestorType is not implemented.
(i.e., alike --> ItemsSource="{Binding FlipViewData, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type AdaptativeGridView}}}" )
<DataTemplate x:Key="AdaptativeGridView_ItemTemplate">
<Grid
Background="White"
BorderBrush="Black"
BorderThickness="1">
<FlipView Width="300" Height="300"
ItemTemplate="{StaticResource FlipView_ItemTemplate}"
ItemsSource="{x:Bind Path=ViewModel.FlipViewData}"/>
</Grid>
</DataTemplate>
I am curious since I am not explicitely setting the x:DataType in the DataTemplate. So, why the DataContext is not the same as the AdaptativeGridView or any "normal" control in the Page?
Option 2, which explicitely sets x:DataType of the DataTemplate (note this is indeed working in the other DataTemplate "FlipView_ItemTemplate"). However, for the "AdaptativeGridView_ItemTemplate", there is a runtime error.
Exception thrown: 'System.InvalidCastException' in UWP.Blank.Mvvm.App01.exe
and no more clues.
<DataTemplate x:Key="AdaptativeGridView_ItemTemplate" x:DataType="vm:PrincipalViewModel">
<Grid
Background="White"
BorderBrush="Black"
BorderThickness="1">
<FlipView Width="300" Height="300"
ItemTemplate="{StaticResource FlipView_ItemTemplate}"
ItemsSource="{x:Bind FlipViewData}"/>
</Grid>
</DataTemplate>
Any experience or suggestions are welcome.
Related
I have a very simple usercontrol:
<UserControl x:Class="PointOfSale.UserControls.HousesGrid"
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">
<ItemsControl x:Name="LayoutRoot" ItemsSource ="{Binding PopularHouses}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ToggleButton
Content="{Binding FormattedPanelTimeRemaining}"
Style="{StaticResource MetroToggleButtonStyle}"
Height="45"
Width="80"
VerticalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As you can see the ItemSource property is bound to the PopularHouses property on the ViewModel of the parent. This works great. However, what I want to do is set the ItemSource of the LayoutRoot element to a different property at the point on the parent form where the control is inserted in the XAML.
The end result should then be multiple instances of this user control, bound to several different properties on the parent's datacontext.
Could someone explain how to implement this?
You just have to bind your UserControl's DataContext to the datacontext of the first ContentControl using RelativeSource.
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}"
I have made the following sample:
The mainwindow XAML
<Window x:Class="WpfDataContext.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfDataContext"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<local:UserControl1/>
</Grid>
</Window>
We set its datacontext to Self just for the purpose of this sample. In codebehind we define a simple property to show how it works:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public string SomeString { get; set; } = "Hello";
}
Then, the usercontrol XAML:
<UserControl x:Class="WpfDataContext.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type ContentControl}}}">
<Grid>
<TextBlock Text="{Binding SomeString}"/>
</Grid>
</UserControl>
Note how we bind its DataContext property since this is the key.
I use a Textblock for simplicity, but the principle applies for your case also
In WPF it was a bit more confusing how to bind colors, like background color to a viewmodel property.
Are there other ways to bind Colors in Avalonia ?
Or is this example a good way ?
Avalonia View
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Button.Views.MainWindow"
Title="Button" Width="700">
<StackPanel Grid.Column="2" Orientation="Vertical" Gap="8" Margin="10">
<TextBox Name="Textbox3" Text="{Binding Textbox3Text}" Foreground="{Binding Textbox3Foreground}"/>
</StackPanel>
</Window>
Avalonia ViewModel
public class MainWindowViewModel
{
private IBrush _textbox3Foreground;
public IBrush Textbox3Foreground
{
get { return _textbox3Foreground; }
set
{
this.RaiseAndSetIfChanged(ref _textbox3Foreground, value);
}
}
public MainWindowViewModel()
{
Textbox3Foreground = Brushes.DarkOliveGreen;
}
}
Make sure that you have set the DataContext of the window to an instance of your view model class:
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Button.Views.MainWindow"
Title="Button" Width="700">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<StackPanel Grid.Column="2" Orientation="Vertical" Gap="8" Margin="10">
<TextBox Name="Textbox3" Text="{Binding Textbox3Text}" Foreground="{Binding Textbox3Foreground}"/>
</StackPanel>
</Window>
In general you don't usually define UI related things such as colours in the view model though. These kind of things are usually defined directly in the view without any bindings. But you can certainly bind to a Brush property like this.
I have a MainWindow.Xaml file. And one usercontrol PatientWindow.Xaml. How to load the patient window in mainwindow in MVVM architecture?
MainWindow.Xaml
<Window x:Class="PatientAdminTool.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:PatientAdminTool.ViewModel"
xmlns:v="clr-namespace:PatientAdminTool.View"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
ResizeMode="CanResizeWithGrip"
WindowStyle="None"
WindowState="Normal"
Title="PatientAdmin Tools"
Height="750"
Width="1400"
AllowsTransparency="True" MouseLeftButtonDown="OnMouseLeftButtonDown" BorderThickness="1" BorderBrush="#555252" >
<WindowChrome.WindowChrome>
<WindowChrome
CaptionHeight="0"
/>
</WindowChrome.WindowChrome>
</Window>
PatientWindow.xaml
Usercontrol window is mentioned in below
<UserControl x:Class="PatientAdminTool.View.PatientWindow"
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:vm="clr-namespace:PatientAdminTool.ViewModel"
xmlns:v="clr-namespace:PatientAdminTool.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" LastChildFill="True" Height="40" Background="#646161" >
<StackPanel DockPanel.Dock="Left" Orientation="Horizontal">
<TextBlock Margin=" 10,5,0,0" HorizontalAlignment="Center" Text="Patients" FontSize="16" TextAlignment="Center" VerticalAlignment="Center" Foreground="#FFFFFF"/>
</StackPanel>
<StackPanel Margin="10,10,0,0" DockPanel.Dock="Right" Background="Transparent" Orientation="Vertical" HorizontalAlignment="Right" VerticalAlignment="Top" Width="50" >
<StackPanel Background="Transparent" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Focusable="False" ToolTip="Close" VerticalAlignment="Center" Background="#646161" BorderThickness="0" BorderBrush="Transparent" Padding="-4" Click="Button_Click" >
<Button.Content>
<Grid Width="45" Height="23">
<TextBlock Foreground="White" Text="r" FontFamily="Marlett" FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</Button.Content>
<Button.Template>
<ControlTemplate TargetType="Button">
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
</StackPanel>
</DockPanel>
</Grid>
</UserControl>
So I need to load the patient window above of main window using MVVM. Here I need to write load event in corresponding View Model. Please help me to do this.
Just add a ControlPresenter at MainWindow.
<ContentPresenter Content="{Binding YouTypeHere}">
<ContentPresenter.Resources>
<DataTemplate DataType="{x:Type fristViewModel1Type}">
<youControlForViewModel1 />
</DataTemplate>
<DataTemplate DataType="{x:Type secondViewModel2Type}">
<youControlForViewModel2 />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
And you could change Views via binding different VM to ContentPresenter.
So you want to open a new child window using MVVM? I assume you would open it from MainWindowViewModel.
Solution 1: without strict MVVM
Sometimes its fine to open it directly from the ViewModel:
private void OnOpenPatientWindowCommandExecute()
{
var o = new PatientWindow();
o.ShowDialog();
}
For this you would have to change PatientWindow from UserControl to a Window.
Solution 2: strict MVVM
The solutions following strict MVVM are a little more complex. In the solution I write here you would have to use a Service, add it to your MainWindowViewModel and bind a control from the view to a Command in the ViewModel. Also, its written as if you are using Dependency Injection, that's why you see the service injected in the constructor. You can avoid this by just instantiating the service in the constructor.
MainWindowViewModel.cs
using Prism.Wpf.Commands; // For easy commands
using PatientAdminTool.Services; // Where you put your new service
public class MainWindowViewModel
{
private IShowDialogService _ShowDialogService;
public MainWindowViewModel(IShowDialogService showDialogService)
{
_ShowDialogService = showDialogService;
// Or do: _ShowDialogService = new ShowDialogService();
// But that's not a good practice and won't let you test
// this ViewModel properly.
OpenPatientWindowCommand = new DelegateCommand(OnOpenPatientWindowCommandExecute);
}
public ICommand OpenPatientWindowCommand { get; private set; }
private void OnOpenPatientWindowCommandExecute()
{
_ShowDialogService.ShowPatientWindow();
}
}
Services\IShowDialogService.cs
public interface IShowDialogService
{
void ShowPatientWindow();
void ShowOtherWindow();
// ...
}
Services\ShowDialogService.cs
public class ShowDialogService : IShowDialogService
{
public void ShowPatientWindow()
{
var patientWindowViewModel = new PatientWindowViewModel();
var patientWindow = new PatientWindow();
patientWindow.DataContext = patientWindowViewModel;
patientWindow.ShowDialog();
}
public void ShowOtherWindow()
{
// Other window ...
}
}
Finally, you make the connection in the View like this:
MainWindow.xaml
<Window
xmlns:vm="clr-namespace:PatientAdminTool.ViewModel">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Button Command="{Binding OpenPatientCommand}">Open Patient</Button>
</Grid>
</Window>
Haven't tried it in Visual Studio yet but that's the idea.
If it's the only thing you are going to be displaying in the same window, just put your view in there like any other control:
<Window x:Class="PatientAdminTool.MainWindow"
xmlns:v="clr-namespace:PatientAdminTool.View"... >
<WindowChrome.WindowChrome>
<WindowChrome
CaptionHeight="0"/>
</WindowChrome.WindowChrome>
<v:PatientWindow/>
</Window>
Do you have a root ViewModel for your window? If so, you can bind to a PatientViewModel:
<v:PatientWindow DataContext="{Binding PatientViewModel}"/>
If not, it's common to set the first DataContext to a ViewModel in the code-behind like this:
<v:PatientWindow Name="PatientWindowView"/>
and:
public partial class Window
{
public MainWindow()
{
InitializeComponent();
PatientWindowView.DataContext = new PatientWindowViewModel();
}
}
If you want to display more than one View, use a ContentPresenter like Shakra has answered.
If you want to open a new window, use what Alberto Cardona López has suggested.
I do not understand why when I toggle the expanding of a tab's treeview in WPF, that it then affects the expanding of all tab's treeviews. I want each tab's treeview to be independent from one another. It's a very simple MVVM setup with a few classes.
Here are the files from the project
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:data="clr-namespace:WpfApplication1"
xmlns:view="clr-namespace:WpfApplication1.View"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="250">
<Window.DataContext>
<data:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--<Button Content="Add" Command="{Binding AddCommand}" Grid.Row="0"></Button>-->
<TabControl x:Name="tabControl1" ItemsSource="{Binding TabItems}" Grid.Row="1" Background="LightBlue">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" VerticalAlignment="Center"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<view:TabItemView />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
TabItemView.xaml
<UserControl x:Class="WpfApplication1.View.TabItemView"
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.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Content}" />
<TreeView Grid.Row="1" Background="Transparent">
<TreeViewItem Header="Favorites">
<TreeViewItem Header="USA"></TreeViewItem>
<TreeViewItem Header="Canada"></TreeViewItem>
<TreeViewItem Header="Mexico"></TreeViewItem>
</TreeViewItem>
</TreeView>
</Grid>
</UserControl>
ViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public class ViewModel
{
private ObservableCollection<TabItem> tabItems;
public ObservableCollection<TabItem> TabItems
{
get { return tabItems ?? (tabItems = new ObservableCollection<TabItem>()); }
}
public ViewModel()
{
TabItems.Add(new TabItem { Header = DateTime.Now.ToString("Tab 1"), Content = DateTime.Now.ToString("F") });
TabItems.Add(new TabItem { Header = DateTime.Now.ToString("Tab 2"), Content = DateTime.Now.ToString("F") });
TabItems.Add(new TabItem { Header = DateTime.Now.ToString("Tab 3"), Content = DateTime.Now.ToString("F") });
}
}
public class TabItem
{
public string Header { get; set; }
public string Content { get; set; }
}
}
The TabControl reuses the same content element for each item. All it does is change the bound data context (i.e. your view model), updating the elements within the templated element that are bound to the view model. So the other state of the view remains the same as you switch from one tab to the next.
It is possible to force a new content element to be created each time you change the tab; one way of doing this is to declare the content element's template as a resource, add x:Shared="False" to the resource declaration, and then use the resource as the value for a Setter applied in a Style targeting the TabItem type.
Going through the setter to apply the template to each TabItem is required — and by TabItem here I mean the WPF TabItem, not your view model class, the name of which I'd change, to avoid confusion, if I were you. Using x:Shared won't help if you just set the TabControl.ContentTemplate directly once.
For example:
<TabControl.Resources>
<DataTemplate x:Key="tabItemTemplate" x:Shared="False">
<l:TabItemView />
</DataTemplate>
<s:Style TargetType="TabItem">
<Setter Property="ContentTemplate" Value="{StaticResource ResourceKey=tabItemTemplate}"/>
</s:Style>
</TabControl.Resources>
However, this has the opposite effect: rather than keeping the state for each item as you switch from tab to tab, the view state is reset entirely, because a whole new content element is created every time you switch.
If this is acceptable to you, then that will work fine. If however you are looking for each tab to retain whatever configuration it was in when the user last viewed that tab, you will have to preserve that state yourself. For example, you could add a bool property to your view model to remember the current setting for each item and bind that to the IsExpanded property for the top-level TreeViewItem:
View model:
public class TabItem
{
public string Header { get; set; }
public string Content { get; set; }
public bool IsExpanded { get; set; }
}
View:
<UserControl x:Class="TestSO33125188TreeViewTemplate.TabItemView"
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.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Content}" />
<TreeView Grid.Row="1" Background="Transparent">
<TreeViewItem Header="Favorites" IsExpanded="{Binding IsExpanded, Mode=TwoWay}">
<TreeViewItem Header="USA"></TreeViewItem>
<TreeViewItem Header="Canada"></TreeViewItem>
<TreeViewItem Header="Mexico"></TreeViewItem>
</TreeViewItem>
</TreeView>
</Grid>
</UserControl>
Note that for this to work, you need to explicitly set the binding Mode property to TwoWay, as the default is OneWay and won't otherwise copy the current TreeViewItem.IsExpanded value back to the view model.
Note also that for your specific example, you can get away with a simple container view model, without implementing INotifyPropertyChanged or similar mechanism. WPF is forced to copy the updated property value back to the view whenever the current tab is changed. But personally, I would go ahead and add the INotifyPropertyChanged implementation; it would be too easy to find yourself reusing the technique in a different scenario where WPF doesn't automatically detect that you've updated the property value, causing a frustrating bug that takes a while to track down and fix.
I use Caliburn Micro for my WPF application. I implemented a little UserControl:
<UserControl Name="ImageButtonUserControl"
x:Class="SportyMate.Utility.Controls.ImageButton"
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">
<Grid>
<Button>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ElementName=ImageButtonUserControl, Path=Image}" />
<TextBlock Text="{Binding ElementName=ImageButtonUserControl, Path=Text}" />
</StackPanel>
</Button>
</Grid>
</UserControl>
Now I want to use these Control in my view:
<uc:ImageButton Name="Cancel" Image="/Images/Icons/cancel_16x16.png" Text="Abbrechen" Margin="3" />
When I want to open my view (in my case it's opened as a dialog) it doesn't work. The View does not openend.
When I remove the Name-Attribute everthing is fine, but the Button have no binding to an action. Can anyone tell me what I have to do for a correct binding? A regular Button worked.
You are on a completely wrong way. You shouldn't create a user control for such a simple modification. You need to create a DataTemplate and use it for a Button.ContentTemplate. First you need to define a helper type for button content:
public class ImageButtonContent
{
public BitmapImage Image { get; set; }
public string Label { get; set; }
}
After that you can use it for DataTemplate:
<Window x:Class="Trys.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Trys="clr-namespace:Trys"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<Trys:ImageButtonContent x:Key="YourImageButtonHere"
Label="Your ImageButtonHere">
<Trys:ImageButtonContent.Image>
<BitmapImage UriSource="your-icon.png" />
</Trys:ImageButtonContent.Image>
</Trys:ImageButtonContent>
<DataTemplate x:Key="ImageButton"
DataType="{x:Type Trys:ImageButtonContent}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Image}"
Margin="5" />
<TextBlock Text="{Binding Label}"
VerticalAlignment="Center"
Margin="10,0,0,0" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Button ContentTemplate="{StaticResource ImageButton}"
Content="{StaticResource YourImageButtonHere}"
Height="50"
Width="250" />
</Grid>
</Window>
I used a resource for an object, but you can use a property on your ViewModel. The result is:
And this just a normal button. You can use for it all power of Caliburn.Micro conventions like Click event default binding. Enjoy!