Caliburn Micro and ModernUI Examples/Tutorials - c#

does anyone have an example or tutorial on how to use Caliburn Micro together with ModernUi (https://mui.codeplex.com)?

Ok so I had a quick mess about with it and a look on the Mui forums and this seems to be the best approach:
Since the window loads content from URLs you need to take a view-first approach, and then locate the appropriate VM and bind the two.
The best way to do this appears to be via the ContentLoader class which is used to load the content into the ModernWindow when it is requested. You can just subclass DefaultContentLoader and provide the necessary CM magic to bind up loaded items:
public class ModernContentLoader : DefaultContentLoader
{
protected override object LoadContent(Uri uri)
{
var content = base.LoadContent(uri);
if (content == null)
return null;
// Locate the right viewmodel for this view
var vm = Caliburn.Micro.ViewModelLocator.LocateForView(content);
if (vm == null)
return content;
// Bind it up with CM magic
if (content is DependencyObject)
{
Caliburn.Micro.ViewModelBinder.Bind(vm, content as DependencyObject, null);
}
return content;
}
}
Your CM bootstrapper should just bootstrap a ModernWindow viewmodel which is backed by a ModernWindow based view (CM tries to use EnsureWindow which creates a new basic WPF Window class, unless of course your control already inherits from Window which ModernWindow does. If you need all dialogs and popups to be MUI you might need to reimplement WindowManager):
public class Bootstrapper : Bootstrapper<ModernWindowViewModel>
{
}
Which can be a conductor (OneActive) and looks like this:
public class ModernWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
}
And XAML for the view is
ModernWindowView.xaml
<mui:ModernWindow x:Class="WpfApplication4.ViewModels.ModernWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mui="http://firstfloorsoftware.com/ModernUI"
Title="ModernWindowView" Height="300" Width="300" ContentLoader="{StaticResource ModernContentLoader}">
<mui:ModernWindow.MenuLinkGroups>
<mui:LinkGroupCollection>
<mui:LinkGroup GroupName="Hello" DisplayName="Hello">
<mui:LinkGroup.Links>
<mui:Link Source="/ViewModels/ChildView.xaml" DisplayName="Click me"></mui:Link>
</mui:LinkGroup.Links>
</mui:LinkGroup>
</mui:LinkGroupCollection>
</mui:ModernWindow.MenuLinkGroups>
</mui:ModernWindow>
Obviously you need to make the loader a resource too:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.xaml" />
<ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Dark.xaml"/>
<ResourceDictionary>
<framework:ModernContentLoader x:Key="ModernContentLoader"></framework:ModernContentLoader>
<wpfApplication4:Bootstrapper x:Key="Bootstrapper"></wpfApplication4:Bootstrapper>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Here's the ChildViewModel I'm using as a test:
public class ChildViewModel : Conductor<IScreen>
{
public void ClickMe()
{
MessageBox.Show("Hello");
}
}
And the XAML for that (just a button)
<UserControl x:Class="WpfApplication4.ViewModels.ChildView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525">
<Grid>
<StackPanel>
<TextBlock >Hello World</TextBlock>
<Button x:Name="ClickMe" Width="140" Height="50">Hello World</Button>
</StackPanel>
</Grid>
</UserControl>
And the proof of concept:

I create a very, very simple sample of chat app using Modern UI for WPF, Caliburn Micro and MEF.
https://github.com/gblmarquez/mui-sample-chat
I hope it helps

Related

Adding resources to an instance of WPF window

I'm making an AutoCAD .net program that has a WPF window as the interface. Currently the WPF interface is being referenced into the AutoCAD .net aplication and I'm calling the window from AutoCAD as follows.
public class Class1
{
public static WPFWindow.MainWindow mainWindow = new WPFWindow.MainWindow();
[CommandMethod("Launch", CommandFlags.Session)]
public void Launch()
{
Autodesk.AutoCAD.ApplicationServices.Application.ShowModalWindow(mainWindow);
}
}
This works fine until I start adding any form of resource to the WPF window I'm adding in. eg The following works until
<Window x:Class="WPFWindow.MainWindow"
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:local="clr-namespace:WPFWindow"
mc:Ignorable="d"
Title="Test" Height="450" Width="800"
WindowStyle="None"
AllowsTransparency="True"
>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Button Content="Press Me"/>
</Grid>
....I reference a static resource style for the window
WindowStyle="None"
AllowsTransparency="True"
Style="{StaticResource MainWindow}"
>
With the static resource when I run the "Launch" command in AutoCAD the program fails to find the static resource. I'm unsure how to get the instance of the WPFWindow to find the resource using C# code. As a test I added the WPFWindow as a reference to a WPF application and managed to get it to find the resource using the Pack URI
<ResourceDictionary Source="pack://application:,,,/WPFWindow;component/Themes/Styles.xaml"/>
Is there a C# equivalent of that I can use for the instance of the WPFWindow.MainWindow?
I managed to get it to work by adding the resources to the window I was referencing in its code behind files.
namespace WPFWindow
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.Resources.Source = new Uri(#"pack://application:,,,/WPFWindow;component/Themes/Styles.xaml"", UriKind.Absolute);
InitializeComponent();
}
}
}
I think this allowed the static resources to be loaded in before they were called for the window.
You cant use Staticresource in the root tag for external Resourcedictionaries. At time of Initialization the resource is not present. Linking it in ctor before calling InitializeComponent (as you did) does actually the same...
...
WindowStyle="None"
AllowsTransparency="True"
Style="{DynamicResource MainWindow}"
...
will work.

Put a View Control in its ViewModel

I'm developing a Windows 8.1 Store app with C# and .Net Framework 4.5.1.
I'm trying to bind Password.SecurePassword to a ViewModel, and reading this SO answer I found a way to do it: Put the PasswordBox in my ViewModel.
But I don't know how to do it. I know how to bind Dependency Properties, but I don't know how to put that control on my ViewModel. This is my XAML:
<Page
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"
DataContext="{Binding MainViewModel, Source={StaticResource Locator}}"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<PasswordBox x:Name="userPassword" />
</Grid>
</Page>
What do I have to do?
You have several options but I'll just give you the basic option without third party libraries.
In your Page constructor. You can do something like this.
public Page()
{
var mainViewModel = this.DataContext as MainViewModel;
if(mainViewModel != null)
{
mainViewModel.PasswordBox = userPassword;
}
}
You can also set it on the Loaded event of the View and set the PasswordBox to the ViewModel.

Triggering code-behind events with InputBindings

Got stuck while adding key-shortcuts to an MVVM app. Searched for a solution but could not find an example where triggering a command was hindered by the DataContext. This leads me to think I'm perhaps trying to do this the wrong way.
See the following example.
Window XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
Title="Stackoverflow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Window.InputBindings>
<KeyBinding Key="F" Modifiers="Control"/>
</Window.InputBindings>
<Grid>
<Button Content="Interface Action" Click="ButtonBase_OnClick"/>
</Grid>
</Window>
Window Code-behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
// Do Work
}
}
Trying to link the input binding to the button, what is the best design choice? These are the options I can think of:
Setup code-behind to be the Data-Context, create a code-behind property ViewModel and define the path deeper in the XAML tree
Write a key-press event in the code-behind and trigger the event from there
Both options seems to be either aesthetically wrong or cause a lot of extra code to be written. What would be the best option from an MVVM point of view?

Add a New ViewModel to ViewModelLocator in MVVM Light Toolkit

I know this question is somewhat basic, but at the current moment, I am completely lost as to how I should add a new ViewModel to my ViewModelLocator class in MVVM Light Toolkit.
My current implementation looks like so:
First assume that I have a Window named Settings, a ViewModel named SettingsViewModel and a ViewModelLocator ViewModelLocator.
First I call CreateSettings() in the VieModelLocator constructor:
public ViewModelLocator()
{
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
CreateSettings();
}
CreateMain();
}
Note that this will always run as I'm not using blend and build the application each time I try to run it. Now for the `CreateSettings() method.
I had no idea what I was doing so I tried to play it safe and model everything after the methods used for creating and managing the MainViewModel.
public static void CreateSettings()
{
if (_settings == null)
{
_settings = new SettingsViewModel();
}
}
Then another few methods modeled after those used for the MainViewModel:
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SettingsViewModel Settings
{
get
{
return SettingsStatic;
}
}
public static SettingsViewModel SettingsStatic
{
get
{
if (_settings == null)
{
CreateSettings();
}
return _settings;
}
}
And in my Settings Window Xaml:
<Window x:Class="_5500A_Auto_Calibrator.Settings"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Settings" Height="300" Width="300"
DataContext="{Binding Source={StaticResource Locator}, Path=Settings}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
The window is then opened from my MainViewModel like so:
Settings settings = new Settings();
settings.Show();
If I try this, I receive an exception:
"'Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '4' and line position '39'."
And an inner exception of:
"Cannot find resource named 'Locator'. Resource names are case sensitive."
I've read up on errors involving a Window's inability to find the Locator resource, but most have to do with blend.
My current take is that I'm doing something wrong, but there's so little documentation as to adding new ViewModels that I'm unsure what I'm doing wrong.
Edit:
My App.Xaml:
<Application x:Class="_5500A_Auto_Calibrator.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:_5500A_Auto_Calibrator.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
StartupUri="MainWindow.xaml"
mc:Ignorable="d">
<Application.Resources>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True" />
</Application.Resources>
</Application>
This is how it usually looks:
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
}
else
{
SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
...
}
the ViewModelLocator is static, yours doesn't seem to be. It usually sits in the ViewModel folder (assuming you installed mvvmlight with the nuget and then added a new wvvm project.
it then proceeds to have the 2 cases for design and for runtime. (if you don't use it, you can skip the if (IsInDesignMode) ... bit, and just put your logic. (though it's a shame, since it's nice to have a preview of some fake data in VS designer ...)
Adding new ViewModels usually involves creating a property of that type, and registering them with the locator, so you can then retrieve them ... but this differs and can be done differently I believe ...
Hope this help, and if there's anything else I can help with, do let me know.
Your data context binding tries to apply earlier, than resource is declared. Try to declare binding this way (of course, this should help only if either MainSkin.xaml or application resources contain Locator resource):
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<Binding Source="{StaticResource Locator}" Path="Settings"/>
</Window.DataContext>

Access a textbox declared in App.xaml (WpfNotifyIcon Tooltip) from another window

I am using WpfNotifyIcon, I have declared it as a resource like this:
<Application x:Class="NotifyIconScratchPad2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:tb="http://www.hardcodet.net/taskbar"
StartupUri="MainWindow.xaml">
<Application.Resources>
<tb:TaskbarIcon x:Key="MyNotifyIcon" IconSource="Icons/stopwatch_start.ico" ToolTipText="Hello world" >
<tb:TaskbarIcon.TrayToolTip>
<TextBlock x:Name="ChangeThis" Text="Hello world" />
</tb:TaskbarIcon.TrayToolTip>
</tb:TaskbarIcon>
</Application.Resources>
</Application>
To use this, I declare it in MainWindow.xaml.cs:
public TaskbarIcon tb;
public Window1 myWindow;
public MainWindow()
{
InitializeComponent();
tb = (TaskbarIcon) FindResource("MyNotifyIcon");
}
How can I access the textbox ChangeThis from another window?
The ultimate answer is setting up an event to do so.
The Model-View-View Model pattern is a good way to accomplish this.
Basically, you have a class that implements the INotifyPropertyChanged interface and a two-way data binding between both text boxes and the data source.
you can use FindName method:
TextBox txtToChange= tb.FindName("txt_ChangeThis") as TextBox;

Categories