Modify WPF Window when Submenu opens without closing Submenu - wpf

I have a Window with a Menu on it. When the Menu is opened, I would like to change the Window's appearance to look disabled. Simply covering it with a gray Rectangle looks nice. Here is the Window markup:
<Grid>
<!--Content-->
<ContentControl Content="{Binding CurrentViewModel}" />
<!--Container to hide content-->
<Rectangle x:Name="Disabler" Fill="#77000000" Visibility="{Binding DisableWindow, Converter={StaticResource BoolToVisibilityConverter}}" />
</Grid>
I tried to set DisableWindow to true when the Submenu opens and false when it closes. However, setting this value seems to close the Submenu. How can I ensure the Submenu stays open?
private void MenuItem_SubmenuOpened(object sender, RoutedEventArgs e)
{
MainWindowViewModel mainVM = Window.GetWindow(this).DataContext as MainWindowViewModel;
if (mainVM != null)
{
mainVM.DisableWindow = true;
}
}
Edit: Since the Rectangle gets set to Visible, the MouseUp event is happening on Disabler. This is why the Submenu closes on me. I tried setting IsHitTestVisible="False" on the Rectangle, but that makes everything under it clickable. Is there a way to keep the Rectangle from stealing focus?

Instead of overlapping the grid with Rectangle, I divided my 2 half.
Is the Menu bar (10 % of screen)
Rectangle area (90% of screen)
Xaml of the screen
<Window x:Class="WpfApp4.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:WpfApp4"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" x:Name="Window1"
Title="MainWindow" Height="450" Width="800">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Border>
<Border.Effect>
<BlurEffect Radius="{Binding ElementName=Window1,Path=DataContext.Radius}" KernelType="Gaussian"/>
</Border.Effect>
<Menu Grid.Row="0" x:Name="Menubar" HorizontalAlignment="Left" Height="24" Margin="10,10,0,0" VerticalAlignment="Top" Width="772" >
<MenuItem Header="Home" SubmenuOpened="MenuItem_SubmenuOpened" SubmenuClosed="MenuItem_SubmenuClosed" >
<MenuItem Header="Office" >
<MenuItem Header="Ground Floor"/>
</MenuItem>
<MenuItem Header="Exit" />
</MenuItem>
</Menu>
</Border>
<Rectangle Grid.Row="1" x:Name="Disabler" Fill="{Binding ElementName=Window1, Path=DataContext.BackGroundColor}" />
</Grid>
As you can see in the Xaml, I have used 2 events SubmenuOpened and SubmenuClosed.
These 2 Methods are responsible to flip the rectangle fill Brush color.
In ViewModel/CodeBehind, I have created 1 property called BackGroundColor, which will be having white color when menu is not clicked and will have grey if we click on Menu.
BackGroundColor will be binded with the Rectangle's Fill property.
Code Behind
public partial class MainWindow : Window,INotifyPropertyChanged
{
private Brush _backGroundcolor;
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int _radius { get; set; }
public int Radius
{
get
{
return _radius;
}
set
{
_radius = value;
NotifyPropertyChanged(nameof(Radius));
}
}
public Brush BackGroundColor
{
get
{
return _backGroundcolor;
}
set
{
_backGroundcolor = value;
NotifyPropertyChanged(nameof(BackGroundColor));
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void MenuItem_SubmenuOpened(object sender, RoutedEventArgs e)
{
BackGroundColor = new SolidColorBrush(Colors.Gray);
Radius = 5;
}
private void MenuItem_SubmenuClosed(object sender, RoutedEventArgs e)
{
BackGroundColor = new SolidColorBrush(Colors.White);
Radius = 0;
}
}
Check out the Menu clicked image below.

I had to go with setting IsHitTestVisible="False" on the Rectangle, even though that makes everything under it clickable. It's a hack, and I would love a better fix.

Related

How to programmatically set UserControl to Topmost?

How to programmatically set UserControl to Topmost of pc screen. I have multiple usercontrol in my wpf application, when I resize any usercontrol, I want to show this usercontrol top of the screen.
i want to show this usercontrol top of the screen.
If by that you mean "top of the screen" inside your application, then that is achieved by using Panel's ZIndex attached property.
Xaml :
<Grid x:Name="LayoutRoot">
<UserControl x:Name="TopMostUserControl"
Margin="10,140,106,48"
Panel.ZIndex="1"
Background="Green" />
<UserControl x:Name="SecondUserControl"
Margin="39,50,37,87"
Panel.ZIndex="0"
Background="red" />
</Grid>
C# :
public MainWindow()
{
InitializeComponent();
Panel.SetZIndex(TopMostUserControl, 1);
Panel.SetZIndex(SecondUserControl, 0);
}
<Grid x:Name="LayoutRoot">
<UserControl x:Name="TopMostUserControl"
Margin="10,140,106,48"
Background="Green" />
<UserControl x:Name="SecondUserControl"
Margin="39,50,37,87"
Background="red" />
</Grid>
Result :
However, if you mean to topmost that UserControl on the entire screen, then that would be something different, you should create another Window that hosts your topmost UserControl and you should change its TopMost property to true when you resize your other UserControls.
MainWindow :
<Grid x:Name="LayoutRoot">
<UserControl x:Name="FirstUserControl"
Margin="10,140,106,48"
Background="Green"
MouseDown="FirstUserControl_OnMouseDown" />
<UserControl x:Name="SecondUserControl"
Margin="39,50,37,87"
Background="red" />
</Grid>
Code Behind :
public partial class MainWindow : Window
{
public TopMostWindow TopMostWindow;
public MainWindow()
{
InitializeComponent();
TopMostWindow = new TopMostWindow();
TopMostWindow.Show();
}
private void FirstUserControl_OnSizeChanged(object sender, SizeChangedEventArgs e)
{
TopMostWindow.Topmost = true;
}
private void MainWindow_OnContentRendered(object sender, EventArgs e)
{
FirstUserControl.SizeChanged += FirstUserControl_OnSizeChanged;
SecondUserControl.SizeChanged += FirstUserControl_OnSizeChanged;
}
//This is to simulate the resizing
private void FirstUserControl_OnMouseDown(object sender, MouseButtonEventArgs e)
{
FirstUserControl.Width = 400;
}
}
TopMostWindow :
<Window x:Class="MvvmLight1.TopMostWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TopMostWindow"
Width="300"
Height="300"
Topmost="False"
WindowStyle="None">
<Grid>
<UserControl x:Name="TopMostUserControl" Background="Blue" />
</Grid>
</Window>
It depends in which container you are using it. Or in what scenario. Basically in Grid you just need to specify it as a last element under Grid container. Otherwise use Panel.ZIndex="1" on the UserControl declaration in XAML

WPF - Page navigation

I am creating a WPF Application in VS 2012.
My MainWindow acts as a header/footer wrapper for the pages that are navigated to.
Within my footer I have added custom back/forward buttons, but I want their visibility to change based off of the CanGoBack and CanGoForward properties.
I can create C# routines that will hide/show the buttons but the function only runs once at initialization. I need these functions to fire off every time a new page is loaded. Any ideas?
Is is a simple example where I use Grids as Pages and i use a Grid array to save the instances of the grids. I hope this would help in your scenario.
XAML:
<Window x:Class="PageNavigation.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>
<Grid Name="page1" Background="Beige">
<Label VerticalAlignment="Center" HorizontalAlignment="Center">Page1</Label>
</Grid>
<Grid Name="page2" Background="Blue" Visibility="Hidden">
<Label VerticalAlignment="Center" HorizontalAlignment="Center">Page2</Label>
</Grid>
<Grid Name="page3" Background="Green" Visibility="Hidden" >
<Label VerticalAlignment="Center" HorizontalAlignment="Center">Page3</Label>
</Grid>
<Grid Name="page4" Background="Cyan" Visibility="Hidden">
<Label VerticalAlignment="Center" HorizontalAlignment="Center">Page4</Label>
</Grid>
<Button Name="btn_Next" HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Next" Click="btn_Next_Click"/>
<Button Name="btn_Previous" HorizontalAlignment="Left" VerticalAlignment="Bottom" Content="Previous" Click="btn_Previous_Click"/>
</Grid>
</Window>
C#:
public partial class MainWindow : Window
{
Grid[] pages;
int activePage = 0;
public MainWindow()
{
InitializeComponent();
pages = new Grid[4];
pages[0] = this.page1;
pages[1] = this.page2;
pages[2] = this.page3;
pages[3] = this.page4;
}
void Next() {
if (activePage<pages.Length-1)
{
pages[activePage].Visibility = Visibility.Hidden;
activePage++;
pages[activePage].Visibility = Visibility.Visible;
}
}
void Previous()
{
if (activePage > 0)
{
pages[activePage].Visibility = Visibility.Hidden;
activePage--;
pages[activePage].Visibility = Visibility.Visible;
}
}
private void btn_Previous_Click(object sender, RoutedEventArgs e)
{
Previous();
}
private void btn_Next_Click(object sender, RoutedEventArgs e)
{
Next();
}
}

How to show a warning window before opening a combobox items list?

My requirement is to show a warning window (in some specific conditions) when clicking on a WPF combobox, just before it shows a list of available items to choose from. The window asks the user if he proceeds or not.
The problem is that after showing this warning window, the combobox popup that should appear to select an item is not open, no matter if I set the property IsDropDownOpen to do so. See the code for details.
<Window x:Class="ComboBoxTester.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">
<StackPanel>
<ComboBox Height="20" PreviewMouseDown="ComboBox_PreviewMouseDown">
<ComboBoxItem>Item 1</ComboBoxItem>
<ComboBoxItem>Item 2</ComboBoxItem>
<ComboBoxItem>Item 3</ComboBoxItem>
</ComboBox>
<CheckBox x:Name="warningConditionCheckBox" >Is warning condition?</CheckBox>
</StackPanel>
</Window>
and the code behind contains:
namespace ComboBoxTester {
using System.Windows.Input;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void ComboBox_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (warningConditionCheckBox.IsChecked == true)
{
// Warn about this situation
var window = new MyDialog { Owner = GetWindow(this) };
// Confirm to proceed
if (window.ShowDialog() != true) {
e.Handled = true;
}
else {
comboBox.IsDropDownOpen = true;
}
}
}
}
}
MyDialog is just a Dialog Window:
<Window x:Class="ComboBoxTester.MyDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyDialog" Height="150" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<Border Background="Silver">
<TextBlock Text="Warning! Sure to proceed?" TextAlignment="Center"/>
</Border>
<StackPanel Grid.Row="1">
<Button Width="100" Content="OK" IsDefault="True" Click="ButtonOkClick"/>
<Button Width="100" Content="Cancel" IsCancel="True"/>
</StackPanel>
</Grid>
</Window>
namespace ComboBoxTester {
using System.Windows;
/// <summary>
/// Interaction logic for MyDialog.xaml
/// </summary>
public partial class MyDialog
{
public MyDialog()
{
InitializeComponent();
}
private void ButtonOkClick(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
}
My idea is to use the WPF combobox to deal with this requirement. And to not create another control, if this is possible. So... How can I see the list of items from this combobox after showing a window? Any suggestion helps.
This is often an issue in WPF that while you are in an event handler you can set other UI controls' properties but they are not processed properly.
But you can invoke your change via the dispatcher, which will queue your request in the UI Message Queue, where it is properly processed after your event handler:
Instead of
comboBox.IsDropDownOpen = true;
use:
Action action = () => comboBox.IsDropDownOpen = true;
Application.Current.Dispatcher.BeginInvoke(action);

WPF ScrollViewer/Canvas mouse event handler

I've created the following control:
<UserControl x:Class="FooBar.AnnotationControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="400" Width="500" >
<ScrollViewer Height="400" Width="500">
<Canvas Height="400" Width="500" Name="ctlCanvas" MouseLeftButtonDown="MouseLeftButtonDownHandler" >
<Canvas.RenderTransform>
<ScaleTransform x:Name="ZoomTransform" />
</Canvas.RenderTransform>
</Canvas>
</ScrollViewer>
</UserControl>
namespace FooBar
{
public partial class AnnotationControl : UserControl
{
public AnnotationControl()
{
InitializeComponent();
}
private void MouseLeftButtonDownHandler( object sender, MouseButtonEventArgs args)
{
//Do Something
}
}
}
when I click the canvas, I don't hit breakpoints in the MouseLeftButtonDownHandler. I even attach this handler to the ScrollViewer and get the same result. Any idea what's going on here?
The default background for a Canvas is Transparent, which allows hit tests to pass through it. To make your Canvas register for HitTests, give it a Background Color.
<Canvas Background="White" ... />

How to create controls from code in a custom control?

In MainPage.xaml.cs (Silverlight Application) I can do something like this:
StackPanel myStackPanel = new StackPanel();
Button myButton = new Button();
myButton.Content = "Button";
myButton.Width = 200;
myButton.Height = 30;
Button myButton1 = new Button();
myButton1.Content = "Button 1";
myButton1.Width = 200;
myButton1.Height = 30;
myStackPanel.Children.Add(myButton);
myStackPanel.Children.Add(myButton1);
this.LayoutRoot.Children.Add(myStackPanel);
What is the equivalent of this code in a custom control when I'm trying to create these controls from the code?
Update:
My question is probably too confusing. I'l try better formulation.
So, I have
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DemoAddControlLib">
<Style TargetType="local:DemoControlShowtime">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:DemoControlShowtime">
<Grid x:Name="LayoutRootControl">
<Button x:Name="Button1" Content="Hi" Width="150" Height="30"></Button>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And code:
DemoControlShowtime.cs
[TemplatePart(Name = "Button1", Type=typeof(Button))]
public class DemoControlShowtime : Control
{
public DemoControlShowtime()
{
this.DefaultStyleKey = typeof(DemoControlShowtime);
}
// Events
public override void OnApplyTemplate()
{
Button1 = (Button)GetTemplateChild("Button1");
}
private Button button1;
private Button Button1
{
get { return button1; }
set
{
if (button1 != null)
{
Button1.Click -= new RoutedEventHandler(myButton_Click);
}
button1 = value;
button1.Click += new RoutedEventHandler(myButton_Click);
}
}
void myButton_Click(object sender, RoutedEventArgs e)
{
Button1.Content = "Hello Button";
}
}
If I click on Button1 the Content changes from "Hi" to "Hello Button". I want, when Button1 is clicked, to add StackPanel with two buttons as its Children into the Grid LayoutRootControl.
I know there is Visibility property and put it into the xaml would be easier but I'm curious how to do it from the code.
I hope this is much clearer than the question was before.
The code isn't really any different to what you have. The only variation is that the field LayoutRoot is not created for you.
However with this line of code:-
Grid LayoutRoot = GetTemplateChild("LayoutRootControl") as Grid;
The rest of your code would be identical (although you should test whether LayoutRoot is null first).
It appears to me that your are just wondering how to use a custom control in multiple places.
I've created a custom control (MyCustomControl) that has the StackPanel shown in your code, then used it multiple times on the MainPage.
MyCustomControl.xaml
<UserControl x:Class="SilverlightApplication2.MyCustomControl"
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"
mc:Ignorable="d">
<StackPanel>
<Button Content="Button 1" Height="30" Width="200"/>
<Button Content="Button 2" Height="30" Width="200"/>
</StackPanel>
MyCustomControl.xaml.cs
public partial class MyCustomControl : UserControl
{
public MyCustomControl()
{
InitializeComponent();
}
}
Then I've used that custom control twice in the main view.
MainPage.xaml
<UserControl x:Class="SilverlightApplication2.MainPage"
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"
mc:Ignorable="d"
xmlns:local="clr-namespace:SilverlightApplication2"
d:DesignHeight="300" d:DesignWidth="400">
<StackPanel>
<local:MyCustomControl Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<local:MyCustomControl Margin="10" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
MainPage.xaml.cs
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
}
Output

Resources