I am quite new to WPF, coming from the Delphi world. I solved the problem below (albeit painfully) in the Delphi world, and hope there is a more elegant solution in the WPF world.
I need to read in an XML file containing a menu "tree", which has the window names in it as well as the menu prompts, and then be able to "show" a window based on having its name.
For example, a segment of the menu, with two choices, might have XML like this:
<MenuLeaf>
<Header>Product information</Header>
<MenuLine>
<Prompt>Product Master File</Prompt>
<WindowName>Products.xaml</WindowName>
</MenuLine>
<MenuLine>
<Prompt>Inventory Data</Prompt>
<WindowName>Inventory.xaml</WindowName>
</MenuLine>
</MenuLeaf>
So when the user makes the "Inventory Data" choice, I will know that I want to do a "show" of the window Inventory.xaml ..... but I only have the literal string "Inventory.xaml".
I will have hundreds of these forms, and the XML file can vary from time to time - so it's not effective for me to have the standard code of
Dim window as New Inventory
window.Show
for each of the several hundred windows.
What I need is something that does
Dim window as New {go out and find the Inventory file with name Inventory.xaml}
window.Show
I have searched endlessly for this with no luck.
I think the path to solution is to use Reflection, which will allow you to dynamically find/invoke your classes. Say your Namespace is MyNs, then you must have a 'Products' Class within it that correspond to the 'Products.xaml' file. To find it, use MyFoundType = MyNs.GetType("Products")
Then get default (or other if you like) constructor for this type : MyFoundType.GetConstructor(). Then invoke the constructor (with arguments if needed) --> you now have your window as an Object.
Cast it to a window and call its Show method, and you're done.
http://msdn.microsoft.com/en-us/library/y0cd10tb.aspx
http://msdn.microsoft.com/en-us/library/h93ya84h.aspx
http://msdn.microsoft.com/en-us/library/6ycw1y17.aspx
You need to use the XamlReader object, which parses XAML at run-time and creates the object.
var rdr = XmlReader.Create(File.Open("Inventory.xaml"));
var window = XamlReader.Load(rdr) as Window;
window.Show();
The XamlReader.Load will return whatever the actual top-level element in the XAML specifies; if it's a Window you can just .Show it. If it's something else, you'll need a container to place it in. For example, you might have a Window with a Border element in it and do:
var control = XamlReader.Load(rdr) as UserControl;
var window = new MyHostWindow();
window.ContentBorder.Child = control;
If you don't actually know the type of element in your XAML you can usually use FrameworkElement, which is the base class for all the visual elements, though you won't get Window-specific behavior from that.
Related
I've been working with Event driven MVVM for a couple of weeks (first time using a design pattern in F#) and I like the idea of separating view and model and also the "functional" controller. But when going through a book on WPF I get the feeling it would be easier if I could adress events directly. Also in some situations I need to get a hold on a control from code behind.
More specific:
How to close a window defined as usercontrol in a XAML file
It seems there would be less need for buttons (triggering booleans that hold state), with a more automated feel as result, if I could directly adress events
Does anybody share this experience or am I still missing something? Is it advisable to go back to FsXaml or polyglot MVVM?
Turns out the code is actually very easy to extend. Based on demos I was able to turn a textbox into a numeric one. The code that does this is very basic, but my intent was to define a custom event accessor. Which can be done by:
Extend UserControl.xaml header with:
xmlns:fsxaml="http://github.com/fsprojects/FsXaml"
fsxaml:ViewController.Custom="{x:Type views:CompositionUserControl}"
And replace the original code in UserControl.xaml.fs:
namespace Space.Views
open FsXaml
type UserView = XAML<"View/UserControl.xaml", true>
type CompositionUserControl () =
member __.ViewModel = Space.ViewModels.UserControlViewModel(Space.Models.Handling.proces)
with
namespace Space.Views
open FsXaml
open System
type UserView = XAML<"View/UserControl.xaml", true>
type CompositionUserControl () =
inherit UserControlViewController<UserView>()
let numeric (txt : string) =
try txt |> int with
| :? System.FormatException -> 0
| _ -> 1
override this.OnLoaded view =
view.Box.PreviewTextInput.Add(fun e -> if numeric e.Text = 0 then e.Handled <- true)
member __.ViewModel = Space.ViewModels.UserControlViewModel(Space.Models.Handling.proces)
EDIT
Looking back at this post, here's my progress regarding my initial questions:
How to close a window defined as usercontrol in a XAML file
Using an attached property DialogCloser.
It seems there would be less need for buttons (triggering booleans
that hold state), with a more automated feel as result, if I could
directly adress events
The key here is to learn:
How to truly separate View(Model) from Model
How to use XAML to exploit its full power
With my old programmes of multiple-windows, I used to use this parameter
wnd.Show(Me)
This code kept wnd above the first Window. Anyway in WPF it gives error
"Too many arguments for 'Public Sub Show'"
How can I do in WPF having the same advantages?
You can set the Window.Owner property in WPF to accomplish this:
wnd.Owner = Me
wnd.Show()
The function you're trying to use doesn't exist. The Window.Owner property is what you're looking for.
wnd.Owner = Me
wnd.Show()
You might also want to be using Window.ShowDialog if you're trying to keep it on top and modal - "This code kept wnd above the first Window" makes it sound like you're probably going for modal.
I have a RichTextBox that I wish to fill with RTF text at design time. This does not mean doing this:
richTextBox1.Rtf = #"<a bunch of rich text>";
which actually assigns the value at run time (or does it?).
I have created a project resource file called "TextResources.resx" with a resource named InstructionsRTF with a Value containing the rich text. How is this to be bound to the RichTextBox at design time?
Edited to add:
#hans-passant is correct, although the exact code I ended up using differs somewhat:
rtfInstructions.Rtf = TextResoures.InstructionsRTF;
where TextResources is the TextResources.resx in the project.
RichTextBox doesn't support binding. If it is already a precooked resource then trying to support this at design time doesn't make sense. It is just one line of code in the form constructor:
public Form1() {
InitializeComponent();
richTextBox1.Rtf = Properties.Resources.instructionsRTF;
}
If you want to get more adventurous at design time then that's possible too. You can create a UITypeEditor that lets you edit the RTF at design time. Code is here.
In my WPF application, I have a table which stores the frequently used window names for each user. At runtiime, I make a list of it.
List<string> LstUserWindows= new List<string>();
What I need is I need to open each window depending on the names of the windows in the list. (I am using usercontrols as windows). Something like below:
foreach (var rec in LstUserWindows)
{
UserControl mainUC = this.FindName(rec.MyWindow) as UserControl;
displayUserControls(mainUC,null);
}
I'm not sure which approach you have currently taken in storing the UserControl instances, but here are two possible approaches you could take.
If all UserControl instances already exist within your UI but are simply hidden, then you should be able to use the FindName(...) (as you've mentioned in your question) and then change the Visibility property of the UserControl.
If you have not yet loaded the UserControl instances and you want to dynamically create the control given it's name, then you need to look into using Reflection. Using this approach, you can acquire the Type information from the Assembly and work on using Reflection to construct the object. Alternately, you could use the Activator class to construct an instance of the required control type. For that approach, you would do something like this.
foreach (var rec in LstUserWindows)
{
UserControl control = (UserControl)System.Activator.CreateInstance("AssemblyName", rec);
displayUserControls(control, null);
}
Note: I'm not sure if the parameter structure is correct (I cannot currently test it). Check out the MSDN Documentation for more help on it.
I'm trying to implement the following: I have an Items Manager, that has an Item class inside. Item class can store two possible visual representations of it - BitmapImage(bitmap) and UserControl(vector).
Then later, in the game, I need to share the same image or vector control between all possible places it takes place. For example, consider 10 trees on the map, and all point to the same vector control. Or in some cases this can be bitmap image source.
So, the problem is that BitmapImage source can be easily shared in the application by multiple UIElements. However, when I try to share vector control, it fails, and says Child Element is already a Child element of another control. I want to know how to organize this in the best way. For example replace UserControl with other type of control, or storage, however I need to be sure it supports Storyboard animations inside.
The code looks like this:
if (bi.item.BitmapSource != null)
{
Image previewImage = new Image();
previewImage.Source = bi.item.BitmapSource;
itemPane.ItemPreviewCanvas.Children.Add(previewImage);
} else
if (bi.item.VectorSource != null)
{
UserControl previewControl = bi.item.VectorSource;
itemPane.ItemPreviewCanvas.Children.Add(previewControl);
}
Or it is not possible to share same control in different places, then what is the best way to make a copy, or the best way to store vector data.
Thanks in advance
So, I found the problem. It is possible to attach the same UserControl to different controls.
However, when on update I was deleting control, and then filling up it again with a new pointer, that sometimes was the same as before deleting, somehow it was still in memory. And so it was like 2 same user control attached to the same parent.
I added a line of code that was cleaning all children in control, before updating it with new vector UserControl, and now works like a charm.