Setting WindowStyle and AllowTransparency in usercontrol - c#

I have a simple usercontrol in my MainWindow. I am trying to create a simple Window Template that is easily protable. I have a usercontrol and this seems to serve my purpose so far well...kinda...
If I set AllowTransparency and WindowStyle in the usercontrol the project compiles successfully but in the MainWindow in the control I get Object Not Set to Instance of... And the entire control is underlined. All good if I set in MainWindow. I can live with this but not the desired result.
xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cntl="clr-namespace:Rtg"
Title="MainWindow" Height="400" Width="525">
<!-- if I add windowstyle and allowtransparency here all good -->
<Grid>
<cntl:test1 WinTitle="test window framing">
<cntl:test1.PlaceHolder1>
<Grid Background="Orange">
<TextBox BorderBrush="Transparent" Text="Content in placehoder:" Height="35" Width="175" Margin="0,10,270,0" Background="Transparent" FontSize="16" ></TextBox>
<Button Width="100" Height="35" Content="click me" FontSize="16"></Button>
</Grid>
</cntl:test1.PlaceHolder1>
</cntl:test1>
</Grid>
</Window>
C#
public test1() {
Window win = Application.Current.MainWindow;
win.WindowStyle = System.Windows.WindowStyle.None;
win.AllowsTransparency = true;
InitializeComponent();
ctrTest2.Title = WinTitle;
}
Been web developing for the last 15 years WPF is somewhat pretty new to me.
Is this Normal Behavior for WPF?
Is there away Around This so I can set window Properties in the usercotrol?

public test1() {
InitializeComponent();
ctrTest2.Title = WinTitle;
//Need to do it after Initialization
Window win = Application.Current.MainWindow;
win.WindowStyle = System.Windows.WindowStyle.None;
win.AllowsTransparency = true;
}
A more "WPF" way of handling it would be to create a ViewModel and Bind the properties
public class MainViewModel : INotifiyPropertyChanged{
private WindowStyle _windowStyle;
public WindowStyle WinStyle {
get{
return _windowStyle;
}
set{
_windowStyle = value;OnPropertyChanged("WinStyle");
}
}
}
And in the XAML
WindowStyle="{Binding Path=WinStyle}"

Found the Error. I was sending one of my functions an int so that I knew which window was loading. Because no windows or variables are initialized when the app is not running, Visual Studio's decided that my integer was out of the bounds of the array. Adding a simple if statement around the above code fixed all issues. Picky Picky WPF.
Kevin, I was unsure of what you meant by View Model. After Reading some the xaml.cs or UserControl in my case would be the ViewModel correct?
here is my fix.
if (LoadingWindow > -1 && Application.Current.Windows.Count > LoadingWindow) {
// Load Window
}

Related

How do I reference a custom property value in a WPF UserControl via Binding?

I'm building a WPF app with custom UserControls, and I'm trying to understand how property bindings are supposed to work. I can't get even the most basic binding to work, and it's simple enough to distill into a tiny example, so I figured someone with more WPF experience might be able to put me on the right track.
I've defined a custom UserControl called TestControl, which exposes a Foo property, which is intended to be set in XAML whenever a UserControl is placed.
TestControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace BindingTest
{
public partial class TestControl : UserControl
{
public static readonly DependencyProperty FooProperty = DependencyProperty.Register("Foo", typeof(string), typeof(TestControl));
public string Foo
{
get { return (string)GetValue(FooProperty); }
set { SetValue(FooProperty, value); }
}
public TestControl()
{
InitializeComponent();
}
}
}
The markup for TestControl just defines it as a control with a single button, whose label text displays the current value of the Foo property:
TestControl.xaml
<UserControl x:Class="BindingTest.TestControl"
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:local="clr-namespace:BindingTest"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Button Content="{Binding Foo}" />
</Grid>
</UserControl>
In my MainWindow class, I just place a single instance of TestControl with its Foo property set to "Hello".
MainWindow.xaml
<Window x:Class="BindingTest.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:BindingTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:TestControl Foo="Hello" />
</Grid>
</Window>
I would expect that when I build and launch this app, I'd see a window with a single button reading "Hello". However, the button is blank: the Binding doesn't seem to work.
If I add a click handler to the TestControl's button, I can verify that the value is being updated behind the scenes:
// Added to TestControl.xaml.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Button clicked; Foo is '{0}'", Foo);
}
// Updated in TestControl.xaml:
// <Button Content="{Binding Foo}" Click="Button_Click" />
When I click the button, I get Button clicked; Foo is 'Hello', but the GUI never updates. I've tried using Path=Foo, XPath=Foo, etc., as well as setting UpdateSourceTrigger=PropertyChanged and verifying updates with NotifyOnTargetUpdated=True... nothing seems to result in the text in the UI being updated to match the underlying property value, even though the property value seems to be getting updated just fine.
What am I doing wrong? I feel like there's just a simple and fundamental misunderstanding in how I'm approaching this.
edit:
Poking around a bit more and reading similar questions has led me to a potential fix: namely, adding a name to the root UserControl element in TestControl.xaml (x:Name="control"), and changing the binding to explicitly specify that control ({Binding Foo, ElementName=control}).
I'm guessing that by default, {Binding Foo} on the Button element just means "find a property named 'Foo' on this Button control", whereas I'd assumed it'd mean "find a property named 'Foo' in the context that this Button is being declared in, i.e. on the TestControl".
Is specifying an explicit ElementName the best fix here?
You have to set the source object of the Binding to the UserControl instance, e.g. like this:
<Button Content="{Binding Foo, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
or
<UserControl ... x:Name="theControl">
...
<Button Content="{Binding Foo, ElementName=theControl}"/>
If you have many such Bindings, you may also set the DataContext of the top level element in the UserControl's XAML to the UserControl instance:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Button Content="{Binding Foo}" />
<Button Content="{Binding Bar}" />
</Grid>
You must however avoid to set the DataContext of the UserControl (which is often recommend by "expert" bloggers), because that would break DataContext-based Bindings of the UserControl properties like
<local:TestControl Foo="{Binding SomeFoo}" />

Dev Express Loading Decorator causing memory leak + run time exception

1) I have some performance problems using dx:LoadingDecorator , in order to demonstrate that I created a small sample app. So the problem is that every time I show a loading splash screen, a new instance of LoadingDecorator is being pushed in the Visual Tree, gets allocated in the memory and it will never be disposed. On the next run, a new instance will be created. This behavior is leading to memory leaks.
The xaml code is :
<dx:DXWindow x:Class="DXApplication12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
Title="MainWindow" Height="800" Width="1525">
<Grid>
<dx:SimpleButton Width="100"
Height="20"
Content="Command"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Command="{Binding SimpleButtonCommand}"/>
<dx:LoadingDecorator
VerticalAlignment="Center"
HorizontalAlignment="Center"
IsSplashScreenShown="{Binding IsBusy}">
<dx:LoadingDecorator.SplashScreenTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="Loading ..." FontSize="30" Background="Transparent"/>
</StackPanel>
</DataTemplate>
</dx:LoadingDecorator.SplashScreenTemplate>
</dx:LoadingDecorator>
</Grid>
and the code behind:
public partial class MainWindow : DXWindow
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
this.ContentRendered += (s, e) =>
{
for (int i = 0; i < 1000; i++)
{
(this.DataContext as MainWindowViewModel).SimpleButtonCommand.Execute(null);
}
};
}
}
public class MainWindowViewModel : ViewModelBase
{
public ICommand SimpleButtonCommand => new DelegateCommand(() => {
this.IsBusy = true;
this.IsBusy = false;
});
private bool _isBusy;
public bool IsBusy
{
get => this._isBusy;
set => this.SetProperty(ref this._isBusy, value, nameof(this.IsBusy));
}
}
2) Another aspect is that randomly, the application raises a run time exception :
An unhandled exception of type 'System.InvalidOperationException' occurred in PresentationFramework.dll
This Visual is not connected to a PresentationSource.
This thing happens only when the application is being started using Visual Studio and if I disable the UI Debugging Tools for XAML the problem dissapear, but I consider this a workaround and I would like to know the root cause :)
I have attached also a photo to describe the two issues that I've encountered : https://ibb.co/iJ057H
Thanks.
I came across this question because I was facing the exact same issues. The second issue (random InvalidOperationException) is also discussed in several thread on the DevExpress support forum (e.g. here and here), but the only workaround suggested by DevExpress is to disable the XAML Debugger.
I worked around these issues by not hiding/showing the LoadingDecorator when the busy state changes, but keeping it 'visible' all the time by setting IsSplashScreenShown="True" and changing the visibility of the LoadingDecorator's content instead. You can use the SplashScreenDataContext property to pass your view model as the data context to the decorator's content to access your IsBusy property.
This way there is only one permanent instance of the decorator, which fixes the memory issue. It also seems to fix the random exception, because that only occurs when the decorator is loaded.
So in your case this would look like this:
<Grid>
<dx:SimpleButton .../>
<dx:LoadingDecorator
...
IsSplashScreenShown="True"
SplashScreenDataContext="{Binding}">
<dx:LoadingDecorator.SplashScreenTemplate>
<DataTemplate>
<StackPanel Visibility="{Binding IsBusy, Converter={StaticResource BooleanToVisibilityConverter}}" ...>
<TextBlock .../>
</StackPanel>
</DataTemplate>
</dx:LoadingDecorator.SplashScreenTemplate>
</dx:LoadingDecorator>
</Grid>

creating a common structure(theme) in WPF window

I just created a windows Form application to inherit controls from base form and it works fine.
In WPF XAML is it possible to inherit controls from a base form to another like above?
When I tried in visual studio, I have got an error showing:"'parentchild.MainWindow' cannot be the root of a XAML file because it was defined using XAML".
My Basewindow cs code:
namespace parentchild
{
public partial class BaseWindow : Window
{
public BaseWindow()
{
InitializeComponent();
}
}
}
My Basewindow xaml code:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxr="http://schemas.devexpress.com/winfx/2008/xaml/ribbon" x:Class="parentchild.BaseWindow"
Title="BaseWindow" Height="350" Width="525">
<Grid>
<StatusBar HorizontalAlignment="Left" Height="35" Margin="0,285,0,0" VerticalAlignment="Top" Width="517">
<Label Content="Label"/>
<Label Content="Label"/>
</StatusBar>
</Grid>
</Window>
My childwindow cs code:
namespace parentchild
{
public partial class childwindow : BaseWindow
{
public childwindow()
{
InitializeComponent();
}
}
}
My childwindow xaml code:
<mn:BaseWindow x:Class="parentchild.childwindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mn="clr-namespace:parentchild"
Title="childwindow" Height="300" Width="300">
<Grid>
</Grid>
</mn:BaseWindow>
I found another solution by creating an user control and applying it to all windows.Is that the right way?
Or anybody have solution for creating a general theme/structure for all Xaml windows.
Please provide me a solution to solve this issue.
You cannot inherit from a class that has been defined in xaml. I would probably go with creating a UserControl and using that as a 'base container' in any Window you want to have the status bar. If you are intent on making a base Window you could try something like this:
Define the base Window in code only:
public class MyWindowBase : Window
{
private ContentControl contentControl;
public MyWindowBase()
{
this.CreateContent();
}
public Object BaseContent
{
get { return this.contentControl.Content; }
set { this.contentControl.Content = value; }
}
private void CreateContent()
{
var grid = new Grid();
var row1 = new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) };
var row2 = new RowDefinition() { Height = GridLength.Auto };
grid.RowDefinitions.Add(row1);
grid.RowDefinitions.Add(row2);
var statusBar = new StatusBar() { Height = 35, Background = Brushes.Blue }; // Initialize the status bar how you want.
Grid.SetRow(statusBar, 1);
this.contentControl = new ContentControl();
grid.Children.Add(this.contentControl);
grid.Children.Add(statusBar);
base.Content = grid;
}
}
Use the base window in xaml like this:
<WpfApplication7:MyWindowBase xmlns:WpfApplication7="clr-namespace:WpfApplication7" x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="500">
<WpfApplication7:MyWindowBase.BaseContent>
<Button>
Something
</Button>
</WpfApplication7:MyWindowBase.BaseContent>
</WpfApplication7:MyWindowBase>
Of course the base class has room for improvement, like making BaseContent a dependency property, but I think it demonstrates the main idea.
It looks like you are basically trying to create a custom control that extends Window. Creating custom controls in WPF is a complex topic because there is a lot to consider when deciding how to implement one. Take a look at this article that talks about this:
Control Authoring Overview
Most likely, you do not actually want a custom control, but rather a custom ControlTemplate (authored in xaml) that redefines how you want your window to look. You can define the template in a Style and apply that style to any Window that you want. It is a lot to try to explain here in an answer, so you should read up on how control templates work and how they are useful.
If you decide you need to add new properties to your custom window, then you will want to make a new class that extends Window and adds those properties as dependency properties (now you have a custom control). You can then use those properties in your custom ControlTemplate to do whatever you want them to do.
Working in WPF is not at all like working in Windows Forms. If you try to apply techniques and practices you learned in Windows Forms to a WPF application, you will cause yourself a lot of headaches. I made that transition a couple years ago, and my advice is to make no assumptions about WPF based on what you know from Windows Forms. They are completely different systems with different ways of doing things.

How main window can catch the NotifyPropertyChanged event of a property in its contained Usercontrol

I have a UserControl, it contained bool property A.
In main window, which contain that UserControl, I have to enable/disable a button depends on the value of A.
I tried to make A as public and binding button like this:
<Button IsEnabled="{Binding MyUserControl.A}"/>
And in UserControl, I set PropertyChangedEventHandler for Property A like:
private bool _a;
public bool A
{
get
{
return _a;
}
set
{
if (_a == value)
return
_a = value;
OnPropertyChanged("A");
}
}
It look fine. But I have no idea why it does not work.
It seems that I have lack of some implementation to communicate between main window and its usercontrol (because with OnPropertyChanged, all binding inside usercontrol work well).
I have some solution for that are
1. Use Messenger to send a message from Usercontrol with the content is value of A, main control will catch & set value to IsEnabled of button.
2. Make a event & raise it anytime value of A is changed.
Do you have any idea about this problems and how to fix it?
Do you think 2 below solutions will work well or you have any other recommendation?
Thanks for reading.
<< Edit >>
This issue is solved. It is my mistake when set the datacontext of usercontrol in code-behind and do not recognize that I already set them in datatemplate.
--> So, duplication make 2 times initialization of viewmodel of usercontrol.
--> somehow, it make the NotifyPropertyChange work incorrectly.
I'm sorry the title of this question is not suitable for this silly mistake. It seems that I went on the right way to solve the question on title.
Thanks for your reading & your advices.
Alternate way to display usercontrol in wpf . Check this StackOverflow discussion here
Alternate for INotifyPropertyChanged : Dependency property for binding.
//usercontrol
public partial class UserControl1 : UserControl
{
public bool A
{
get { return (bool)GetValue(AProperty); }
set { SetValue(AProperty, value); }
}
public static readonly DependencyProperty AProperty =
DependencyProperty.Register("A", typeof(bool), typeof(UserControl1), new UIPropertyMetadata(false));
public UserControl1()
{
InitializeComponent();
DataContext = this;
}
}
}
//Mainwindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication1"
Height="300" Width="300">
<StackPanel>
<src:UserControl1 x:Name="myControl" />
<Button IsEnabled="{Binding A, ElementName=myControl}" />
</StackPanel>
The binding exressions always use the the DataContext as the base for the evaluation of the binding path. So in your case the DataContext must be the window itself, you could set it in the constructor of the window in the code-behind file:
this.DataContext = this;
Also note that to work your window needs to have a property called MyUserControl.
Another option would be to give the MyUserControl instance that you might have instanciated in XAML a name and then use ElementName in the binding expression:
<Grid>
<loc:MyUserControl Name="myUserControl" />
<Button IsEnabled="{Binding A, ElementName=myUserControl}" />
</Grid>
You need to create an instance of UserControl by declaring an it in resources and set it into datacontext and then you can use it. Try this.
<Window.Resources>
<WpfApplication2:UserControl1 x:Key="MyUserControl"/>
</Window.Resources>
<StackPanel DataContext="{StaticResource MyUserControl}">
<Button IsEnabled="{Binding Path=A}" Content="Test" Height="20" />
</StackPanel>

Prevent window from spanning multiple monitors

Is there any simple way to prevent WPF windows from spanning multiple monitors in a dual-monitor setup? With "simple" I mean without writing code-behind that measures the monitor size and assigns a width to the Window.
I would prefer if a window uses only the part available on the current monitor (where "current" means the monitor with the currently focused window). It is OK if the user resizes the window such that it covers both monitors, but at the time when the window opens it shall stay on a single monitor.
Here is an example:
MainWindow.xaml:
<Window x:Class="MultiMonitorTest.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>
<Button Content="Open Window"
Height="70"
HorizontalAlignment="Left"
Margin="165,147,0,0"
Name="button1"
VerticalAlignment="Top"
Width="179"
Click="button1_Click" />
</Grid>
</Window>
MainWIndow.xaml.cs:
using System.Windows;
namespace MultiMonitorTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Window2 win = new Window2();
win.ShowDialog();
}
}
}
Window2.xaml:
<Window x:Class="MultiMonitorTest.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window2"
SizeToContent="WidthAndHeight">
<Grid>
<TextBox x:Name="txt"
Margin="10"
Background="LightPink"
AcceptsTab="True"
AcceptsReturn="True"
TextWrapping="Wrap"/>
</Grid>
</Window>
Window2.xaml.cs:
using System.Windows;
namespace MultiMonitorTest
{
public partial class Window2 : Window
{
public Window2()
{
InitializeComponent();
txt.Text = new string('x', 1000);
}
}
}
When clicking the "Open window" button, the new window opens across two monitors. I would prefer if Window2 stayed completely within the current monitor.
Well, you could use the same width, height (and perhaps position) as the parent window on the second one. Without additional code, I don't think you can really force the window to span across two monitors. You can however, with limited code start it on one instead of two.
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen; // in your Window Xaml
//and combine with
Rectangle workingArea = System.Windows.Forms.Screen.PrimaryScreen.WorkingArea; // Reference System.Windows.Forms and set left, top, width and height accordingly.
This is the best you can do I think without having to write 'large amounts' of code (this will center it to the working area of the primary screen). If you really want to limit your window to span across multiple monitors is of course possible, but requires some work.
A good starting point would be the WindowInteropHelper class.

Categories

Resources