WPF: Add Page w/functionality into a Window at runtime as XAML - wpf

I have created a WPF app where I dynamicly build XAML elements using c# code and then add them to a root "container" grid.
What I'm trying to do is take advantage of the features in Blend and create some XAML Pages that have their own set of code behind logic, Storyboards, etc.
I want to load that XAML at runtime, however for some reason my approach is not working and I'm at a loss for why.
This what what I did before. In my root Window I create a new MyModule and add it to my contentRoot.
myModule = new MyModule();
contentRoot.Children.Add(myModule );
(Approach that works) MyModule class extends Canvas and consists of a .XAML file and .CS code behind file. The XAML is just a root canvas, and the .CS has all the logic to create elements and add them to the root canvas.
When I use this same approach where MyModule is now extends Page nothing shows up. The XAML now has a lot of content in it including a Canvas.Resources Canvas.Triggers, and a bunch of other elements.
How can I load pre-created XAML content from a Class including the code behind logic at run time?

Page and Canvas are two different kind of components in XAML.
Page is framework element, and Canvas is Container, which can have multiple controls placed with absolute x,y coordinates. Where else Page has only one property "Content" you can consider Page being an advanced content control.
Blend must have created methods related to Canvas and which will be like "Canvas.SetLeft" etc, but they will certainly not work in Page.
Your Page class must have one content of type "Canvas" and you must add all controls inside "Canvas" inside page, that shall help you.
This is the text from MSDN,
A Page can have only a single child element. All other elements on a Page must be descendents of that element. Typically, the content of a Page hosts a layout element—such as Grid, StackPanel, and DockPanel—that hosts the content of the Page.
In your case, Page should host one element "Canvas" and add items.
Or why dont you try this one, let your MyModule be same as what it is, and you create a new Page, called MyModulePage and it should look like this.
<MyModulePage>
<MyModule/> <!-- that is your canvas generated in blend -->
</MyModulePage>

I find this question a bit unclear, but here's something that worked for me.
Define MyModule as:
<Page x:Class="WpfApplication3.MyModule"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
</Page>
public partial class MyModule : Page
{
public MyModule()
{
InitializeComponent();
this.Content = new TextBlock(new Run("WOW!"));
}
}
Created a standalone file called MyModuleStandalone.xaml:
<local:MyModule xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:WpfApplication3;assembly=WpfApplication3"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</local:MyModule>
The code below works. When I show c, it displays a text block with the text "WOW!".
FileStream xamlFile = new FileStream("MyModuleStandalone.xaml", FileMode.Open, FileAccess.Read);
MyModule c= (MyModule)XamlReader.Load(xamlFile);
this.Content = c;
The local var c is an instance of MyModule so all of the code from that class is available. Is this what you're looking for?
You cannot use the x:Class attribute in your standalone XAML file because this implies the XAML is a partial class and the rest of the class is declared somewhere else. XamlReader just won't support it.
Remember that when you read in a XAML file, you are reading in a serialized object. There is no way to dynamically inject code behind into an arbitrary standalone xaml file.

FileStream xamlFile = new FileStream("Resources/News/NewsModuleCanvas.xaml", FileMode.Open, FileAccess.Read);
Canvas newsCanvas = (Canvas)XamlReader.Load(xamlFile);
contentRoot.Children.Add(newsCanvas);
Used this to load XAML, however this still does not give me the option of also adding the code behind logic.

Related

Referencing MainWindow's content in MaterialDesign's DialogHost

I'm developing a WPF application using Material Design in XAML library. I'd like to use a dialog box to display error messages. In the documentation I've read that in order to dimm and disable content behind the dialog box I have to put it in the DialogHost tag, right after DialogHost.DialogContent
This is what I have right now:
<Window>
<md:DialogHost>
<md:DialogHost.DialogContent>
Content of my dialog box
</md:DialogHost.DialogContent>
My window's content wrapped in grid.
</md:DialogHost>
</Window>
The problem is: I'm planning to add few more dialog boxes for different purposes and I don't really know how to do that, since I have to put the rest of the code inside the DialogHost tag, which in my opinion would be a bit messy.
Instead I would like to achieve something like this:
<Window>
<Grid>
<md:DialogHost>
<md:DialogHost.DialogContent>
Content of my dialog box
</md:DialogHost.DialogContent>
Reference somehow the rest of the window's content
</md:DialogHost>
Window's content
</Grid>
</Window>
I tried using ContentPresenter but I'm getting error saying that the property Content cannot be bound to visual element.
If the idea described above is impossible to do, how can I use more than 1 dialog boxes? Because nesting one in another would result in a big messy code.
You should first remove the <md:DialogHost.DialogContent>from your main window and create an <UserControl>for each dialog box you need.
In the ViewModel class using such a dialog you must instantiate this <UserControl> and provide this instance as parameter for the DialogHost.Show method.
Dim view As New MyDialog1() With {.DataContext = Me}
Dim obj as Object = Await MaterialDesignThemes.Wpf.DialogHost.Show(view)
if obj IsNot Nothing Then
'do something
EndIf
I this (VB) example an MyDialog1 View class is instantiated using the DataContext of the VieModel class allowing the View class to access ViewModel class properties. Then the DialogHost.Show method is invoked. The View class can provide user response which is evaluated after closig of the View class.

Convert UserControl to ResourceDictionary

I'd like to try out this globalization thing in WPF:
http://www.codeproject.com/Articles/515960/Globalization-in-WPF-using-ResourceDictionary
Unforunately the second step got me already it says: convert UserControl to ResourceDictionary
How can I make this happen?
Thanks!
First you create a simple UserControl and name it however you like.
Then you open the .xaml file and change <UserControl to <ResourceDictionary.
Then you have to open de .xaml.cs file of the UserControl and add the two attributes (the ones between brackets []) to the class:
[ExportMetadata("Culture", "en-US")]
[Export(typeof(ResourceDictionary))]
public partial class EnglishLanguage : ResourceDictionary
{
public EnglishLanguage()
{
InitializeComponent();
}
}
And add this using reference to the list of usings:
using System.ComponentModel.Composition;
The compiler will notify you that ExportMetadata, Export and the using reference could not be resolved. That's because you need to add the reference to the composition DLL to your project manually.
In the solution explorer under you project, right click References and click Add reference. A list of available DLL files will pop up under Assembly - Framework.
Check the box for System.ComponentModel.Composition, click OK and build your project.
Now you're all set ;)

Where do XAML files that build as "Page" go?

I've added a XAML file to a Windows Phone 8 project. Its build action is "Page". I want to load the XAML as a text string (to feed into XamlReader.Load()). How can I accomplish this?
It's not available as a separate file in the XAP package; it's probably somewhere in the DLL.
When set to Page, the compiler will compile the XAML into BAML and add the BAML file as a resource to the assembly.
If you wish to get the original XAML back out from the BAML resource at runtime, then you will need to deserialize the BAML, and then serialize your object to XAML.
You can have a look at the Baml2006Reader, or a better option would be to use Application.LoadComponent which is what the InitializeComponent method uses internally. InitializeComponent is called by the partially generated class for your XAML code behind.
var uri = new Uri("/MyAppName;component/MyXaml.xaml", //Note extension: XAML, not BAML
UriKind.Relative);
Page rootObject = new Page(); //Assuming XAML root element is Page - it could be anything
Application.LoadComponent(rootObject, uri);
(assuming the root element of your XAML file is a Page).
You can then serialize the Page to a XAML string using the XamlWriter:
string xaml = XamlWriter.Save(rootObject);
Note that this is the XamlWriter in the System.Windows.Markup namespace, not System.Xaml. If your XAML has WPF types, then you should use this XamlWriter to avoid errors.

Create Silverlight custom control with code only (no xaml)

I would like to create a Silverlight custom control using C# only, without any xaml.
Here is my work so far (stripped down to the bare minimum for the question):
I tried to inherit User control as follows:
public class myControl: UserControl
{
// class code
}
And adding it to the LayoutRoot:
myControl control = new myControl();
LayoutRoot.Children.Add(control);
The control is added, but its invisible!!
How can i make it visible ? Is there something i missed ?
edit: The only visual element in my contorl is a grid with an image background
Your Usercontrol will be empty and have no visual effect until you give it a child control via it's Content property.
Well unless you put a template in place or add elements in code, UserControl is empty.
Maybe you could try inheriting from an existing control which has a template, like Button, etc and change that in code?

Designing WPF UserControl that gets its DataContext from outer controls: How to have some sample data in designer but use inherited DC at runtime?

I am designing a WPF user control which contains other user controls (imagine a WidgetContainer, containing different Widgets) - using M-V-VM architecture.
During development, I have WidgetContainerView in a window, window (View) spawns a WidgetContainerViewModel as its resource, and in a parameterless constructor of WidgetContainerViewModel, I fill its exposed collection with some sample widgets (WidgetViewModels).
WidgetContainer control inherits the DataContext from window, and inside, there is a ListView, that binds Widgets to WidgetView control (which is inside ListView.ItemTemplate).
Now this works OK in my WindowView, as I see my sample widgets, but once I edit the WidgetContainerView or WidgetView, there is no content - at design time, controls are standalone, and they don't inherit any DataContext, so I don't see a content, and have troubles designing them (a ListView is empty, Widget's fields as well...).
I tried adding a sample widget to the WidgetView:
public partial class WidgetView : UserControl
{
public WidgetView()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
//btw, MessageBox.Show(...) here sometimes crashes my Visual Studio (2008), but I have seen the message - this code gets executed at design time, but with some lag - I saw the message on reload of designer, but at that time, I have already commented it - wtf?
this.DataContext = new WidgetViewModel(); //creates sample widget
}
}
}
but that didn't work - I still don't see anything in designer.
I also wanted to create a WidgetViewModel as a resource in WidgetView, like this:
<UserControl x:Class="MVVMTestWidgetsControl.View.WidgetView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="WidgetViewModel" //this doesn't work!
Height="Auto" Width="Auto">
<UserControl.Resources>
<ResourceDictionary>
<ViewModel:WidgetViewModel x:Key="WidgetViewModel" />
</ResourceDictionary>
</UserControl.Resources>
<TextBlock Text="{Binding Path=Title}"></TextBlock>
</UserControl>
but I don't know how to assign a WidgetViewModel as a DataContext of a whole widget - I can't add DataContext attribute to UserControl, because WidgetViewModel is defined later in the code. Any ideas how to do this? I could use a sample data this way, and just override it in code so that it has the right content at runtime...
What are your best practices when developing user controls? Thank you, designing empty control is no fun :)).
In your second snippet, you should be able to refer to your DataContext as a DynamicResource:
DataContext="{DynamicResource WidgetViewModel}"
But most custom user controls have some sort of top level layout container, and you can set the DataContext on that container as a StaticResource.
In your case, however, you may want to consider dropping the VM portion of your code altogether since you're writing a custom UserControl. You should ask yourself what benefits are you gaining from a completely self-contained ViewModel with no real backing Model designed for just one View (i.e. the custom UserControl). Perhaps you could just define some DependencyProperties and use those?
I came up with several solutions: Add DC as resource (it will get automatically instantiated with parameterless constructor), and do the following in View's codebehind:
public PanelView()
{
InitializeComponent();
if (!DesignerProperties.GetIsInDesignMode(new DependencyObject())) //DeleteAtRelease:
{
//we are in runtime, reset DC to have it inherited
this.DataContextHolder.DataContext = DependencyProperty.UnsetValue;
}
}
Better way would be to only assign DC if we are at designtime, but VS didn't like it - it worked only sometimes, and quite nondeterministically, and once it even crashed.
Other check for design time is:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
{
this.DataContext = new WidgetViewModel();
}

Resources