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
Related
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);
}
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.
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.
I am creating one sample WPF application in MVVM pattern to implement master page-content page concept.
In my wpf application I have one MainWindow page and 3 usercontrols say Page1, Page2 and Page3.
The MainWindow.xaml contains oner grid, one stackpanel and one frame.
Grid in MainWindow.xaml contains one image control and the stackpanel contains one TextBlock and 3 buttons.
This frame is filled or loaded first by page as follows:
// Load and show the MainWindow
MainWindowViewModel vm = new MainWindowViewModel();
MainWindow main = new MainWindow();
Navigator.NavigationService = main.NavigationFrame.NavigationService;
main.DataContext = vm;
main.Show();
// Load and navigate to the first page
Page1ViewModel pagevm = new Page1ViewModel();
Page1 p1 = new Page1();
p1.DataContext = pagevm;
Navigator.NavigationService.Navigate(p1);
By using this I am able to navigate from page1 to page2 and then from page2 to page3.
Here my problem is initially the stack panel should be disable means while displaying page1 and page2.
But while navigating from page2 to page3 that means whenever i am showing the page3 the stack panel which was in MainWindow should be enable and the buttons will be able to perform their click events in page3 only.
Please let me know the solution for this problem.
You can extract the current page to the MainWindowViewModel and add a property that returns, whether the StackPanel is enabled:
MainWindowViewModel:
private Page _currentPage = null;
public Page CurrentPage
{
get { return _currentPage; }
set
{
_currentPage = value;
Notify("CurrentPage");
Notify("StackPanelEnabled");
}
}
public bool StackPanelEnabled
{
get { return CurrentPage is Page3; }
}
Notify is a helper method that raises the PropertyChanged event. Bind the Content property of the Frame to the CurrentPage:
<Frame Content="{Binding CurrentPage}"/>
Use it:
Page1ViewModel pagevm = new Page1ViewModel();
Page1 p1 = new Page1();
p1.DataContext = pagevm;
vm.CurrentPage = p1;
I have some itemsControl that i dynamicly adding to it StackPanel as an item.
Each of the StackPanel contain 2 button - btn1, btn2.
I dynamicly connect each button to some button event ( all the button event are same ).
When i test it - i see that the event is not call on the button click - and i dont see any reason to this.
the Code:
private StackPanel CreatePanel()
{
StackPanel stackPanel = new StackPanel();
stackPanel.Orientation = Orientation.Horizontal;
for (int i = 0; i < 2; i++)
{
Button btn = new Button();
btn.Height = btn.Width = 100;
btn.Content = i % 2 == 0 ? "Btn1" : "Btn2";
// Event connection
btn.Click += new RoutedEventHandler( button1_Click );
stackPanel.Children.Add(btn);
}
return stackPanel;
}
From MSDN: For certain combinations of input events and WPF control classes, the element that raises the event is not the first element that has the opportunity to handle it.
This is the nature of the RoutedEventHandler:)
try below code , works for me Please note grd is a Grid on my XAML
Edited added code for ItemsControl
XAML
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<ScrollViewer >
<Grid>
<ItemsControl Name="grd">
</ItemsControl>
</Grid>
</ScrollViewer>
Code
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
grd.Items.Add(CreatePanel()); // Now grd is a itemsControl
}
private StackPanel CreatePanel()
{
StackPanel stackPanel = new StackPanel();
stackPanel.Orientation = Orientation.Horizontal;
for (int i = 0; i < 2; i++)
{
Button btn = new Button();
btn.Height = btn.Width = 100;
btn.Content = i % 2 == 0 ? "Btn1" : "Btn2";
// Event connection
btn.Click += new RoutedEventHandler(doCall);
stackPanel.Children.Add(btn);
}
return stackPanel;
}
private void doCall(object sender, RoutedEventArgs e)
{
MessageBox.Show("Hi");
}
}