How to rebuild logical tree in WPF without showing window - wpf

I have a test code that add a TabItem to TabControl.
But when I try to find the TabItem by name, null is return.
I found a solution is show the window, then I can find the TabItem by name.
But when many tests are running, OutOfMemory exception is occurred because many windows are opened.
Is there another solution to rebuild logical tree without showing window?
The following is my test code
[TestMethod]
public void MyTest2()
{
// Arrange
// Initilize a subVM of CMSEditorViewModel type
var subVM = new SubViewModel();
// Initialize a mainVM of CMSEditorMainViewModel type
var mainVM = new MainViewModel();
// Initialize a MainWindow of DynamicCMS.Exe.CMSEditor
var mainWindow = new MyEditor.MainWindow();
mainWindow.DataContext = mainVM;
ContentPresenter presenter = new ContentPresenter();
using (var stream = System.IO.File.OpenRead(CmsPath.DirViewWithBS + "Subscreen.xaml"))
{
DataTemplate template = XamlReader.Load(stream) as DataTemplate;
presenter.ContentTemplate = template;
presenter.Content = subVM;
}
// Create a TabItem of TabControl
TabItem item = new TabItem();
item.Header = "Tab1";
item.Content = presenter;
item.Name = "tab1";
// Get "mainTabControl" TabControl from MainWindow
CustomTabControl tab = CmsUtil.GetControl((Visual)mainWindow.Content, "mainTabControl") as CustomTabControl;
// Add TabItem to TabControl
tab.Items.Add(item);
mainWindow.Show() // After showing window, I can find the TabItemControl
// Act
TabItem tabItem = (TabItem)CmsUtil.GetControl((Visual)mainWindow.Content, "tab1");
// Assert
Assert.IsNotNull(tabItem);
}

Related

Problems implementing ColorAnimation in Ellipse Fill

I am attempting to implement a ColorAnimation on the Fill of an Ellipse, but not having any success. (I am implementing this ColorAnimation in the code-behind of my app's MainWindow, called via the Messenger pattern from the View Model.):
public partial class MainWindow : Window
{
private SolidColorBrush fillBrush = new SolidColorBrush(Colors.Red);
private ColorAnimation ca = new ColorAnimation(Colors.Red, Colors.Yellow, new Duration(TimeSpan.FromMilliseconds(800)));
private Storyboard flashEllipse = new Storyboard();
public MainWindow(MainWindowVM viewModel)
{
DataContext = viewModel;
InitializeComponent();
Messenger.Default.Register<string>(this, StartFlashEllipse, Constants.StartMsg);
Messenger.Default.Register<string>(this, StopFlashEllipse, Constants.StopMsg);
RegisterName("EllipseFillBrush", fillBrush);
Storyboard.SetTargetName(ca, "EllipseFillBrush");
Storyboard.SetTargetProperty(ca, new PropertyPath("(0).(1)", Ellipse.FillProperty, SolidColorBrush.ColorProperty));
flashEllipse.Children.Add(ca);
flashEllipse.RepeatBehavior = RepeatBehavior.Forever;
}
private void StartFlashEllipse(string ellipseTag)
{
Dispatcher.Invoke(DispatcherPriority.Render, (Action)delegate ()
{
Ellipse ellipseToFlash = <code to find Ellipse in Window>;
ellipseToFlash.Fill = new SolidColorBrush(Colors.Blue);
flashEllipse.Begin(ellipseToFlash);
});
}
...
}
I got the PropertyPath code from this StackOverflow question, but I am still getting a similar Exception on the flashEllipse.Begin() line as the OP of this question:
''Fill' property does not point to a DependencyObject in path '(0).(1)'.'
What am I missing here?
You do not need a Storyboard and a complex PropertyPath.
Just directly start an animation of the Color property of the SolidColorBrush:
ellipseToFlash.Fill = new SolidColorBrush(Colors.Blue);
ellipseToFlash.Fill.BeginAnimation(SolidColorBrush.ColorProperty, ca);

Dynamically load TabItem

I have 2 xaml files: MainWindow.xaml and Subscreen.xaml.
I want to dynamically load Subscreen.xaml as a TabItem under TabControl of MainWindow.xaml.
There are a ViewModel for each View.
MainWindow.xaml: MainViewModel
SubScreen.xaml: SubViewModel
The following is the test code:
[TestMethod]
public void MyTest2()
{
// Arrange
// Initilize a subVM of CMSEditorViewModel type
var subVM = new SubViewModel();
// Initialize a mainVM of CMSEditorMainViewModel type
var mainVM = new MainViewModel();
// Initialize a MainWindow of DynamicCMS.Exe.CMSEditor
var mainWindow = new MyEditor.MainWindow();
mainWindow.DataContext = mainVM;
ContentPresenter presenter = new ContentPresenter();
using (var stream = System.IO.File.OpenRead(CmsPath.DirViewWithBS + "Subscreen.xaml"))
{
DataTemplate template = XamlReader.Load(stream) as DataTemplate;
presenter.ContentTemplate = template;
presenter.Content = subVM;
}
// Create a TabItem of TabControl
TabItem item = new TabItem();
item.Header = "Tab1";
item.Content = presenter;
item.Name = "tab1";
// Get "mainTabControl" TabControl from MainWindow
CustomTabControl tab = CmsUtil.GetControl((Visual)mainWindow.Content, "mainTabControl") as CustomTabControl;
// Add TabItem to TabControl
tab.Items.Add(item);
// Act
CustomTabControl customTabControlEditor = (CustomTabControl)CmsUtil.GetControl((Visual)mainWindow.Content, "EditTabControl");
// Assert
Assert.IsNotNull(customTabControlEditor);
}
The "mainTabControl" is defined in MainWindow.xaml -> I can get this "mainTabControl" TabControl.
The "EditTabControl" is defined in Subscreen.xaml -> I can't get this "EditTabControl" TabControl -> customTabControlEditor is null.
Is anything wrong in the code that loading dynamically?
Thanks in advance.

Window FontSize is not inherited in child control

In certain cases I have the problem that the FontSize that I set on a WPF window is not inherited to a child control.
It happens, if a custom user control sets its content (e.g. a Label) upon changing the DataContext.
I can reproduce this when putting this UserControl into a new window, then close this window and create a new one having the same UserControl in it (see the following code).
In my complex application it's a custom popup window and a custom UserControl that changes its content if the DataContext is changed. There the font is not inherited upon the first open of the window (so the usercontrol hasn't been in another visual/logical tree until that) but I can't reproduce this in a small test application.
public partial class App : Application
{
// App.xaml: ShutdownMode="OnExplicitShutdown"
private void Application_Startup(object sender, StartupEventArgs e)
{
var testControl = new TestControl();
var w = new Window();
w.FontSize = 40;
w.DataContext = this;
w.Content = testControl; // TestControl.DataContextChanged creates label which has FontSize = 40
w.Show();
w.Close();
w.DataContext = null;
//w.Content = null; // if this is done, the font will be correct (40)
w = null;
w = new Window();
w.FontSize = 40;
w.DataContext = this;
//testControl.DataContext = this; // if this is done, the font will be correct (40)
w.Content = testControl; // TestControl.DataContextChanged creates label with remaining FontSize = 12 (Default)
w.Show();
}
}
public class TestControl : UserControl
{
public TestControl()
{
DataContextChanged += TestControl_DataContextChanged;
}
private void TestControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null) Content = new Label() { Content = "TestControllabel"};
else Content = null;
}
}
I'm not looking for a fix of this example app, but for the reason why the font size is not inherited in this special case, so maybe then I can fix my complex app.
Any thoughts would be useful !
Edit: For now I fixed my application by setting the datacontext of the control before setting it as window content.

MainWindow child in wpf application

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
System.Windows.Controls.Button b = new System.Windows.Controls.Button();
System.Windows.Shapes.Rectangle r = new System.Windows.Shapes.Rectangle();
r.Width = 40;
r.Height = 40;
r.Fill = System.Windows.Media.Brushes.Black;
b.Content = r; // Make the square the content of the Button
this.AddChild(b);
}
}
I have code for button from some WPF 4 book, and i want to display from here ( not from XAML), but when i want to add button 'b' as a child of main window i get exception and info : Content of a ContentControl must be a single element.
How can i display it in c#?
As you say this line
this.AddChild(b);
wont work as the error points out it requires a single element (ie Grid, StackPanel)
Give your Grid in xaml a name
MainWindow.xaml
<Grid x:Name="root">
</Grid>
and add your button to the Grid in MainWindow.xaml.cs
//this.AddChild(b); //wont work cant add button to this(MainWindow)
root.Children.Add(b); //adds button to the Grid of MainWindow

WPF/AvalonDock v2.0 LayoutDocument

How can I add programmatically LayoutDocument with some of UIElements inside it? (like stackpanel, scrollviewer etc.) I'd like to add new LayoutDocument with stackpanel, canvas etc. to LayoutDocumentPane when user clicks "New project" button. May I somehow clone xaml code from one LayoutDocument and load it's to new one? And is it possible to bind Title LayoutDocument property to ViewModel Property? ( i get error it has to be dependency property )
You can use Content property. For example if you want to add a new LayoutDocument with a custom content (StackPanel e.g.) you could do it as follow:
//Get the main LayoutDocumentPane of your DockingManager
var documentPane = dockManager.Layout.Descendents().OfType<LayoutDocumentPane>().FirstOrDefault();
if (documentPane != null)
{
LayoutDocument layoutDocument = new LayoutDocument {Title = "New Document"};
//*********Here you could add whatever you want***********
layoutDocument.Content = new StackPanel();
//Add the new LayoutDocument to the existing array
documentPane.Children.Add(layoutDocument);
}
First, in XAML - give the name to the Grid, for example, x:Name = "mainGrid"
Then in class write this
//Create button - we put this in document
Button mybutton = new Button();
mybutton.Content = "hello";
mybutton.Width = 100;
mybutton.Height = 50;
mybutton.Click += (sender, ev) => { MessageBox.Show("Hello"); };
DockingManager dockmanager = new DockingManager();
//Set theme
dockmanager.Theme = new Xceed.Wpf.AvalonDock.Themes.ExpressionLightTheme();
//Create LayoutRoot
var layoutroot = new Xceed.Wpf.AvalonDock.Layout.LayoutRoot();
//Create LayoutPanel
var layoutpanel = new Xceed.Wpf.AvalonDock.Layout.LayoutPanel();
//Create LayoutDocumentPane
var layoutdocpane = new Xceed.Wpf.AvalonDock.Layout.LayoutDocumentPane();
//Create LayoutDocument and set parameters of Document
var LayoutDocument = new Xceed.Wpf.AvalonDock.Layout.LayoutDocument();
LayoutDocument.Title = "Some text";
//Put button in Document
LayoutDocument.Content = mybutton;
layoutdocpane.Children.Add(LayoutDocument);
layoutpanel.Children.Add(layoutdocpane);
layoutroot.RootPanel.Children.Add(layoutpanel);
dockmanager.Layout = layoutroot;
mainGrid.Children.Add(dockmanager);
Sorry for my poor English. Please rewrite this, if it would be helpful.
I'm not that familiar with WPF and especially AvalonDock. I did it like this and it works so far :)
You can write a separate class for your documents that inherits from LayoutDocument. In that way you should be able to edit the standard layout of your "Project-Document" with the VisualStudio Designer (add your stackpanel, canvas etc.).
(I assume that you have a standard way of displaying your "Project-Document". Otherwise you could build the content yourself in code behind like you would do in WPF and put it inside the LayoutDocument.)
For example:
<ad:LayoutDocument x:Class="Namespace.MyDocument"
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:ad="http://avalondock.codeplex.com"
d:DesignHeight="500"
d:DesignWidth="800"
<Grid>
<!-- content -->
</Grid>
</ad:LayoutDocument>
And the class in code behind that inherits from LayoutDocument:
namespace Namespace
{
public partial class MyDocument : AvalonDock.Layout.LayoutDocument
{
// ...
}
}
To create and add a new document you just instantiate a new MyDocument object and add it to the collection via binding or something like layoutDocumentPane.Children.Add(doc).
I don't know about the binding for the Title Property, though.
Thats exactly right.
You can add the title by just adding doc.Title = "My document title"
or
You can add Title="My document title" in the document.xaml which is going to be the child.

Resources