In WinForms, Form had a ClientSize property (inherited from Control), which returns the size of its client area, i.e., the area inside the title bar and window borders.
I'm not seeing anything similar in WPF: there's no ClientSize, ClientWidth, ClientHeight, GetClientSize(), or anything else that I can think to guess the name of.
How do I go about getting the client size of a WPF Window?
One way you could do it is to take the top most child element, cast this.Content to its type, and call .RenderSize on it, which will give you its size.
<Window x:Class="XML_Reader.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="400" Width="600" WindowStyle="SingleBorderWindow">
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
</Grid>
</Window>
((Grid)this.Content).RenderSize.Height
((Grid)this.Content).RenderSize.Width
edit:
as Trent said, ActualWidth and ActualHeight are also viable solutions. Basically easier methods of getting what I put above.
var h = ((Panel)Application.Current.MainWindow.Content).ActualHeight;
var w = ((Panel)Application.Current.MainWindow.Content).ActualWidth;
One way to do it is with the code below. XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
<Canvas>
</Canvas>
</Window>
C#:
using System.Windows;
using System.IO;
using System.Xml;
using System.Windows.Controls;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
double dWidth = -1;
double dHeight = -1;
FrameworkElement pnlClient = this.Content as FrameworkElement;
if (pnlClient != null)
{
dWidth = pnlClient.ActualWidth;
dHeight = pnlClient.ActualHeight;
}
}
}
}
I used a Grid with VerticalAlignment=Top. As a result the Grid unfortunately didn't fill the parent Window anymore (which is its default behaviour, but the VerticalAligment property spoils it).
I solved it by putting an empty Border around the Grid. This border fills the complete content of the window, it has the same dimensions as the default border that a wpf window has anyways.
To get the Grid to fill the main window, I used the binding:
<Border BorderThickness="0" x:Name=Main>
<Grid VerticalAlignment="Top" Height="{Binding ElementName=Main, Path=ActualHeight}"> ...
</Grid>
</Border>
All the suggested solutions are based on the idea to use the size of Windows.Content to know what is the actual size available within the window, like this:
var h = ((Panel)Application.Current.MainWindow.Content).ActualHeight;
This of course only works if Window.Content is not null. Which is a problem if you want to set Window.Content from your code and you already then need to know exactly how much space is available.
The other problem is that the above code only provides the available space once a first layout cycle has completed (i.e. in the Window_Loaded event). But what do you do if you need to know the available space during the first layout cycle, for example because you draw to the window during Windows.OnRender() ?
The first control in the visual tree of any Window is always a Border, even if Window.Content is null. Interestingly, Border.RenderSize has already a value, even when RenderSize.ActualSize might still be zero. I guess the reason is that the size of the Border does not depend on Window.Content, but only on the size of the window (unless, of course, if Window.SizeToContent is used).
I recommend to place your code into the Window.SizeChanged event. Because each time the Window size changes, your content needs to change too. You cannot use the size provided in the event parameters, which gives you the size of the complete window, but you can get the the available size within the window like this:
var h = ((Border)GetVisualChild(0)).RenderSize.Height;
You can use that line of code also if you override Windows.OnRender().
Related
A little bit of background first, I have an application that uses the Microsoft AddIn Framework (MAF) that gets a WPF UI from the plugins (You can follow this Microsoft example to create one). This is up and working fine until the plugin content is large enough that the main form needs to scroll. When this happens, it scrolls outside of the bounds that it should.
In the image, you'll notice that the Plugin Label Top goes over Main Label Top when you scroll down some and at the bottom you'll only see Plugin Label Bottom. My code for the main form is the following:
<Window x:Class="WpfAddinTest.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:WpfAddinTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Background="DarkGray">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition/>
<RowDefinition Height="25"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Main Label Top"/>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ContentControl Name="PluginHolder"/>
</ScrollViewer>
<Label Grid.Row="2" Content="Main Label Bottom"/>
</Grid>
</Window>
ContentControl is what houses the plugin UI, I don't understand why the plugin UI is going over it's parent controls. I've tried housing it in different types of controls, such as a DockPanel and Grid, everything acts the same way.
Is there a special way to get this to function correctly?
If more code is needed, I'm happy to post it, https://github.com/middas/WpfAddInTest is my complete sample project that demonstrates this.
EDIT: Loading up the form in WPF Inspector, all I can see is an AddInHost control, it doesn't show any of the individual controls inside the ContentControl. Does this have something to do with it?
EDIT 2: In trying anything I can think of, I was thinking that maybe it wasn't getting the right height when it placed the control in, so I had the plugin return the desired height and set the Height of the ContentPlaceholder manually based on what was returned; no luck. Here is what I tried:
I updated the AddIn contracts from GetInt() to GetHeight() and on the Plugin I have this method now:
public double GetHeight()
{
_Control.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
return _Control.DesiredSize.Height;
}
Then on the hosting form, I have this now:
public MainWindow()
{
_PluginPath = System.IO.Path.Combine(Environment.CurrentDirectory, "Pipeline");
_AddInPath = System.IO.Path.Combine(_PluginPath, "AddIns");
InitializeComponent();
var warnings = new List<string>(AddInStore.Update(_PluginPath));
_PluginToken = AddInStore.FindAddIns(typeof(IPlugin), _PluginPath, _AddInPath).FirstOrDefault();
_Plugin = _PluginToken.Activate<IPlugin>(AddInSecurityLevel.FullTrust);
var control = _Plugin.GetControl();
PluginHolder.Height = _Plugin.GetHeight();
PluginHolder.Content = control;
}
EDIT 3: Attempting to force the ZIndex doesn't seem to affect it either.
Panel.SetZIndex(control, -1);
I believe I've finally figured out the issue. I believe the problem is because the plugin UI is outside of the AppDomain, the ScrollViewer doesn't know how to clip the content properly. What I finally did that worked was create a callback to the main UI that the plugin can use to be given a height that it needs to fit in. If the plugin requires scrolling, the plugin can then handle it with it's own ScrollViewer.
Here is the updated Contract:
[AddInContract]
public interface IPluginContract : IContract
{
INativeHandleContract GetControl();
double GetHeight();
void SetHostCallback(IHostCallbackContract callback);
}
IHostCallbackContract:
public interface IHostCallbackContract : IContract
{
double GetHeight();
}
Now before the plugin returns the Control it can set the Height given by the main form:
public FrameworkElement GetControl()
{
if (_Callback != null)
{
_Control.SetHeight(_Callback.GetHeight());
}
return _Control;
}
I have updated my Git repo (https://github.com/middas/WpfAddInTest) with the entire working solution. The only issue with it now is that the scrolling isn't double buffered so it flickers. I'll have to live with that though since it doesn't appear there is a way to fix though due to WPF rendering via DirectX.
My application has a lot of windows and most of them share some basic features. Because of that I extended the Window class to create a base for all my windows.
Everything compiles and displays fine but the designer just shows an empty window when I use my window class.
I made a basic example that can be easily used, my real window is much more complex but this shows the problem.
Here is the code:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Markup;
namespace WpfApplication1
{
[ContentProperty("ContentElement")]
public class MyWindow : Window
{
public ToolBar ToolBar { get; private set; }
public StatusBar StatusBar { get; private set; }
public Border ContentBorder { get; private set; }
public UIElement ContentElement
{
get { return (UIElement)GetValue(ContentElementProperty); }
set { SetValue(ContentElementProperty, value); }
}
public static readonly DependencyProperty ContentElementProperty = DependencyProperty.Register(
"ContentElement", typeof(UIElement), typeof(MyWindow),
new PropertyMetadata(null, (d, e) =>
{
MyWindow w = (MyWindow)d;
w.ContentBorder.Child = (UIElement)e.NewValue;
}));
public MyWindow() : base()
{
ToolBar = new ToolBar();
ToolBar.Height = 30;
ToolBar.VerticalAlignment = VerticalAlignment.Top;
StatusBar = new StatusBar();
StatusBar.Height = 20;
StatusBar.VerticalAlignment = VerticalAlignment.Bottom;
ContentBorder = new Border();
ContentBorder.SetValue(MarginProperty, new Thickness(0, 30, 0, 20));
Grid grid = new Grid();
grid.Children.Add(ToolBar);
grid.Children.Add(ContentBorder);
grid.Children.Add(StatusBar);
Content = grid;
}
}
}
XAML example for using MyWindow:
<local:MyWindow x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="300">
<Grid>
<Rectangle Fill="Blue" />
</Grid>
</local:MyWindow>
Doing the exact same thing with a UserControl works just fine, also in the designer. Just replace every occurance of MyWindow with MyUserControl and extend from UserControl if you want to try that.
Is there any way I can get a custom Window like that to work with the designer, or do i have to make a UserControl and use that in every window?
Also, is this some kind of bug or intended behavior?
Addional info: I'm running Visual Studio 2015 Community and I'm using .net 4.6
I Also tried another approach. Instead of using the ContentPropertyAttribute i have overwritten the ContentProperty like this:
new public object Content {
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
new public static DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(BaseUserControl), new PropertyMetadata(null, (s, e) =>
{
MyWindow bw = (MyWindow)s;
bw.ContentBorder.Child = (UIElement)e.NewValue;
}));
Again this works completely fine with a UserControl. With a Window I can at least see the Content in the designer now, but the ToolBar and StatusBar are still not showing up in the designer. When running it everything works correctly.
First, I am no super expert on WPF, but have done a bunch and think I can offer and help clarify some components. First, you can NOT derive from a .XAML based declaration of a WPF-Window, it can only be if entirely within code. I have come to find that sometimes the visual element building is much easier to do in XAML than it is within code, but both can and do work.
So, that said, I would like to offer a solution that might work for you. Starting with WPF Window Style / Templatea, if you are not already familiar with them, along with other controls you can run through their defaults.
First, I am starting with a RESOURCE DICTIONARY STYLE definition that will mimic much of what you may want in your default form. This becomes the stuff within the "ControlTemplate" of the style definition. I have created this as a file "MyWindowStyle.xaml" at the root level WpfApplication1 I created on my machine (just to match your sample project file namespace reference).
Inside the template, you could have almost anything... grids, dock panel, stack panels, etc. In this case, I have used a DockPanel and added your sample ToolBar, StatusBar and two extra labels just for sample. I also preset size and bogus color just to give visualization of the parts when you confirm their impact.
The CRITICAL element to look at is the . This identifies where the content for each of your DERIVED Windows content will be placed... Think of it as a place-holder for each one of your forms for individuality while the rest of the form, its controls all remain consistent. You will see it come into play as you play around with it.
The content of it is and notice the style x:Key="MyWindowStyle". This coincidentally is the same as the xaml, but you could have 100's of styles within a single resource dictionary. I am keeping simple to just the one for demo purposes.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Style x:Key="MyWindowStyle" TargetType="Window">
<Setter Property="SnapsToDevicePixels" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Grid.Background>
<SolidColorBrush Color="{DynamicResource WindowColor}"/>
</Grid.Background>
<AdornerDecorator>
<DockPanel LastChildFill="True" Background="Blue">
<!-- List items docked to the top based on top-most first going down -->
<ToolBar x:Name="tmpToolBar" Height="45" DockPanel.Dock="Top" />
<Label Content="Testing by Style"
Height="30" Width="150" DockPanel.Dock="Top"/>
<!-- When docking to the bottom, start with bottom most working up -->
<StatusBar x:Name="tmpStatusBar" Height="30"
Background="Yellow" DockPanel.Dock="Bottom" />
<Label Content="Footer area based from style"
Height="30" Width="250" DockPanel.Dock="Bottom" />
<!-- This one, since docked last is rest of the space of the window -->
<ContentPresenter DockPanel.Dock="Bottom"/>
</DockPanel>
</AdornerDecorator>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode" Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip"
Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Next, we need to make this publicly available for the entire duration of the application, including availability within the designer mode... Within your projects "App.xaml" which is the startup for the application, it will have a default and empty area. Replace it with this.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/WpfApplication1;component/MyWindowStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Now, to a CODE-ONLY (not a .xaml window based definition) of your "MyWindow.cs" class. If you look at the style where I declared the toolbar and statusbar, I assigned them the names of "tmpToolBar" and "tmpStatusBar" respectively. Notice the [TemplatePart()] declarations. I am now expecting the template to HAVE these controls by the given name within the TEMPLATE somewhere.
Within the constructor, I am loading the Style from the App.xaml resource dictionary being fully available. Then I follow-up with the OnApplyTemplate() which I typically heavily document my code so anyone following me has some idea how / where things originated from and self explanatory.
My entire "MyClass.cs" is below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
namespace WpfApplication1
{
[TemplatePart(Name = "tmpToolBar", Type = typeof(ToolBar))]
[TemplatePart(Name = "tmpStatusBar", Type = typeof(StatusBar))]
public class MyWindow : Window
{
protected ToolBar myToolBar;
protected StatusBar myStatusBar;
public MyWindow() : base()
{
// NOW, look for the resource of "MyWindowStyle" within the dictionary
var tryStyle = FindResource("MyWindowStyle") as Style;
// if a valid find and it IS of type Style, set the style of
// the form to this pre-defined format and all it's content
if (tryStyle is Style)
Style = tryStyle;
}
// the actual template is not applied until some time after initialization.
// at that point, we can then look to grab object references to the controls
// you have need to "hook up" to.
public override void OnApplyTemplate()
{
// first allow default to happen
base.OnApplyTemplate();
// while we get the style loaded, we can now look at the expected template "parts"
// as declared at the top of this class. Specifically looking for the TEMPLATE
// declaration by the name "tmpToolBar" and "tmpStatusBar" respectively.
// get object pointer to the template as defined in the style template
// Now, store those object references into YOUR Window object reference of Toolbar
var myToolBar = Template.FindName("tmpToolBar", this) as ToolBar;
if (myToolBar != null)
// if you wanted to add your own hooks to the toolbar control
// that is declared in the template
myToolBar.PreviewMouseDoubleClick += myToolBar_PreviewMouseDoubleClick;
// get object pointer to the template as defined in the style template
var myStatusBar = Template.FindName("tmpStatusBar", this) as StatusBar;
if (myStatusBar != null)
myStatusBar.MouseDoubleClick += myStatusBar_MouseDoubleClick;
// Now, you can do whatever else you need with these controls downstream to the
// rest of your derived window controls
}
void myToolBar_PreviewMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// in case you wanted to do something based on PreviewMouseDoubleClick of the toolbar
MessageBox.Show("ToolBar: Current Window Class: " + this.ToString());
}
void myStatusBar_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// in case something for MouseDoubleClick on the StatusBar
MessageBox.Show("StatusBar: Current Window Class: " + this.ToString());
}
}
}
So now, lets put it into place. Have your application's main window derive from the MyWindow class. The only thing you need there is
namespace WpfApplication1
{
public partial class MainWindow : MyWindow
{}
}
In the DESIGNER of your form, put in a few controls, such as label, textbox, whatever. You do not see your actual other style yet, but just go with it. Save and run the sample app. Your main window should be displayed with the entire pre-defined template there ALONG WITH the few extra control you had placed specifically on this form.
Now, to get the full visualization in your "MainWindow" from the designer perspective. Within the .xaml area of
<my:MyWindow
x:Class="WpfApplication1.MainWindow"
[other declarations] >
just add the following before the close ">"
Style="{StaticResource MyWindowStyle}"
The resource is available via the App.xaml at startup and you should now be able to see the entire visual while you are designing... but you cant change the outermost template, just the content specific to this one page as mentioned about the "ContentPresenter" part of the template definition. What you are changing is within the place-holder portion allocated by the Template. If you want to change the main part of the window controls, you need to update the TEMPLATE!
But here is part of the trick of the template designer. Start with this, and build in what you need visually, get it placed right and once ready, take it out of here and put into the template and now it is applicable to the rest of all windows. Fix fonts, sizes, colors, whatever.
Hope this helps, and if you have any questions for follow-up, let me know.
Window class is very complex in compare to UserControl class. Microsoft has written more than 8k lines of code in Window class compare to 80 lines in UserControl, additionally Window class contain many operation/event/restriction on content property, and any one piece of code is hindering in rendering the content when you use [ContentProperty("ContentElement")] with the Window subclass MyWindow .
Probably making it a UserControl is better option, If not possible you can write some code temporarily(copy code from ContentElement property) in content property to see the design view.
<lib:MyWindow.Content>
<Button Content="Click" Width="200" />
</lib:MyWindow.Content>
and then just remove the code before run time. (Not a good idea but, whatever works.:) and I suspect that you have already figured that out.
So iv looked around for a bit and found out that MDI is obselete for WPF, Basically what i am trying to do is show a specific page in a grid object on load, and once a menu item from my drop down menu is selected, the content of the grid will be changed to the content from a different page (this is depending on which menu item is selected).
To go into more detail (perhaps this will help) The area where the window will be shown will need to have the window with no borders, or titles, or buttons to minimize/close etc.. only showing the content of this window, it won't be resizeable but fixed, i have a menu of which as i said earlier, when a different menu item is clicked, the relevant window should be displayed in the fixed area. Additionally if any buttons or events inside this content that is displayed happen (i.e a button causes a different window to show for example) then the content in the fixed area should be replaced by this new window's content
This is the first time i have done something like this and from what i've read it sounds like this is something very tricky for a WPF application, I hope i can get some sort of insight or direction i should be going so that i can make this possible.
Thanks.
You can try for example ChildWindow from Extended WPF Toolkit Community Edition.
Edit #1:
But whenever i try to create a WindowContainer in the Xaml it has
problems with the namespace prefix with "xctk:WindowContainer" so
how do i create the appropriate namespace prefix to use it?
You have to add that namespace:
xmlns:xctk=http://schemas.xceed.com/wpf/xaml/toolkit
For example:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow" Height="350" Width="525">
<Grid>
<xctk:WindowContainer>
<xctk:ChildWindow Height="100" Width="250" Left="10" Top="10">
<TextBlock Text="Hello World ..." />
</xctk:ChildWindow>
</xctk:WindowContainer>
</Grid>
</Window>
Edit #2:
You can of course change some properties (for example):
<xctk:ChildWindow
Height="100"
Width="250"
Left="10"
Top="10"
Name="chWindow"
CloseButtonVisibility="Hidden"
WindowStyle="None"
BorderThickness="0">
Edit #3:
Ok yeah, so with everything referenced it is giving me errors still..
Try it simpleā¦ Create Wpf Application, add Extended WPF Toolkit 2.4 NuGet package, in MainWindow.xaml add previous code and in MainWindow.xaml.cs add next code:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
this.chWindow.Show();
}
}
}
I am just in the process of teaching myself WPF. I have reached the point of adding controls dynamically and have hit a brick wall on something really simple. I code that should create a button (shown below):
Button button = new Button() { Height = 80, Width = 150, Content = "Test" };
parentControl.Add(button);
My question is what is parentControl actually called? I am using the standard Visual Studio 2012 WPF template and my main window is called MainWindow. I have no objects in the Window besides what comes in the template
So far I have looked at:
WPF runtime control creation
Dynamic control creation in WPF
WPF MVVM Dynamic control creation
Dynamic creation of control
Where should I put WPF specific code when using MVVM?
Steps Of Control Creation Process WPF
Where to put code in (primarily) windowless WPF app?
The closest I have found it: WPF runtime control creation.
All of these questions just assume you know such a basic thing but I don't. Please help.
I think I understand your question. If your XAML code looks like:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
</Window>
Then your codebehind should be something like:
public MainWindow()
{
InitializeComponent();
Button button = new Button() { Height = 80, Width = 150, Content = "Test" };
//In case you want to add other controls;
//You should still really use XAML for this.
var grid = new Grid();
grid.Children.Add(button);
Content = grid;
}
However, I warmly suggest you to use XAML as much as you can. Furthermore, I wouldn't add controls from the constructor but I'd use the Loaded event of the window. You can add a handler to the event in codebehind from the constructor, or directly in XAML. If you wanted to have the same result as above in XAML, your code would be:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Height="80" Width="180" Content="Test"/>
</Grid>
</Window>
I'm new in WPF and C#. I know a lot of VB.NET and I'm used to the way when I call a form object like textboxes, etc. I'm calling it from another form. Now, I'm using WPF, I'm confused. Because I have a Main Window. And I want to add and item to a listbox in the Main Window from a Class. In VB.Net , its just like this.
IN FORM2
Form1.Textbox.Text = "";
Wherein I can't do it in WPF. Can someone please Help me. Thanks!
WPF windows defined in XAML have their controls publicly accessible from other classes and forms, unless you specifically mark them with the x:FieldModifier attribute as private.
Therefore, if you make an instance of your main window accessible in another class, be it a Window or anything else, you'll be able to populate controls from within this second class.
A particular scenario is when you want to update the contents of a control in your main window from a child window that you have opened on top of it. Is such a case, you may set the child window's Owner property to the current, main window, in order to access it while the child is visible. For instance, let's say you have defined these two windows:
// MainWindow
<Window x:Class="TestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox Name="mainListBox" Height="250" HorizontalAlignment="Stretch" VerticalAlignment="Top"/>
<Button Content="Open Another Window" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="20" Click="OpenAnotherWindow_Click"/>
</Grid>
</Window>
and
// AnotherWindow
<Window x:Class="TestApplication.AnotherWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AnotherWindow" Height="300" Width="300">
<Grid>
<Button Content="Add New Item to Main Window" HorizontalAlignment="Center" VerticalAlignment="Center" Click="AddNewItem_Click"/>
</Grid>
</Window>
each in its own XAML file.
In MainWindow's code behind, inside the button click handler, you show an instance of AnotherWindow as a dialog and set its Owner property to MainWindow's instance:
private void OpenAnotherWindow_Click(object sender, RoutedEventArgs e)
{
AnotherWindow anotherWindow = new AnotherWindow();
anotherWindow.Owner = this;
anotherWindow.ShowDialog();
}
Now, you can access the MainWindow's instance from AnotherWindow's Owner property, in order to add a new item to the ListBox control defined on it, in the button click handler in AnotherWindow's code behind:
private void AddNewItem_Click(object sender, RoutedEventArgs e)
{
MainWindow mainWindow = Owner as MainWindow;
mainWindow.mainListBox.Items.Add(new Random().Next(1000).ToString());
}
It simply adds a new random number to the ListBox, in order to show how the code accesses and modifies the control's data in MainWindow.
Pure WPF solution, but also may be easiest in your case, is using a Data Binding in WPF.
Every form's control is binded to some data on ModelView (pure MVVM approach) or to data (more or less like yuo can do it in WindowsForms). So the "only" thing you have to do is to read/write data binded to controls on UI of that form.
For example, you have TextBox on Windows and want to read a data from it.
This TextBox is binded to some string property of the class that is responsible for holding the data for the controls on that form (just an example, in real world could be 1000 other solutions, based on developer decisions). So what you need, is not to say: "window give textbox" and after read TextBox's content, but simply read binded string property.
Sure it's very simply description of a stuff. But just to give you a hint. Follow databinding link provided above to learn more about this stuff. Do not afraid of a lot of stuff there, it's after all is not a complicated idea and also pretty intuitive. To make that stuff to work in simply case you will not need to make huge efforts by me. The stuff becomes really complex when you end up into real world applications.
This will get all active windows:
foreach (Window item in Application.Current.Windows)
{
}