In my application loading popup is shown every time when I call It. But some times It just doesn't show and it is same situation. Popup is a custom UserControl object made with WPF.
Popup is in MainWindow.xaml:
<Viewbox Stretch="Fill">
<Grid>
<Grid x:Name="Body" Height="768" Width="1024" Background="{StaticResource SCBPassiveColor}" />
<src:InfoPane x:Name="InfoPaneMaster" Visibility="Collapsed" VerticalAlignment="Top" HorizontalAlignment="Center" />
</Grid>
</Viewbox>
In the Body of the main window application loads selected layout/view. Before new layout is loaded program calls :
LayoutCommands.DisplayInfoPane(msgLoginSuccess, ROPInfo.Info, null);
implementation:
public void DisplayInfoPane(string infoText, ROPInfo infoType, int? displayTime)
{
StopTimer();
SetTimer(displayTime ?? DefaultDisplayTime);
PrepareInfoPane(infoText, infoType);
colCloseIcon.Width = new GridLength(0);
this.Visibility = Visibility.Visible;
}
The popup automatically closes (sets visibility to collapsed) after default time.
Why sometimes this popup just doesn't show? Is this associated with rendering?
Related
I am new to C# WPF, I have created a Popup window which contains a DataGrid to hold some data. In certain cases if there are any errors in the data I want to display this error in the same popup window at the bottom of the window (See screenshot). The idea is that the user can then click ok and the message will disappear displaying the full datagrid again.
Does anybody know how to do this?
I do not want another popup message box in a separate window, I want all messages to be displayed/stacked in the same pop window as the datagrid.
Below, there are 2 grids in the XAML: There is is the master grid, which contains the DataGrid, and another grid which contains the error UI. The error grid is normally collapsed (Visibility set to Visibility.Collapsed).
When the error message needs to be shown, the error grid's Visibility is set to Visibility.Visible, which shows the grid. When the user clicks on the "Dismiss" button, the error grid's Visibility is set to Visibility.Collapsed.
There is not a separate window or popup. Everything is contained within the master view.
XAML:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<DataGrid>
</DataGrid>
<Button Grid.Column="1" Content="Show Message Window" VerticalAlignment="Center" Click="Button_Click_1"/>
<!-- This is the "error grid"-->
<Grid VerticalAlignment="Bottom" Height="Auto"
Background="AliceBlue" Visibility="Collapsed" Name="grdError">
<TextBlock Text="Oops. This is an error!"/>
<Button Content="Dismiss" HorizontalAlignment="Right" Click="Button_Click_3"/>
</Grid>
</Grid>
Code Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
grdError.Visibility = Visibility.Visible;
}
private void Button_Click_3(object sender, RoutedEventArgs e)
{
grdError.Visibility = Visibility.Collapsed;
}
}
I have a ToolBar in my application which causes Problems.
I have "DropDown" Buttons inside the ToolBar (ToggleButton + Popup) Those DropDowns work properly if they are on the Visible Part of the ToolBar, they do not work properly if they are located in the ToolBarOverflowPanel.
If i Open a DropDown in the ToolBarOverflowPanel the Popup does not seem to receive focus. There are still hover effects (opposing to the behaviour of the same Popup in the visible toolbar which seems to consume all Mouse Events) and i can still click any other DropDown which opens the next Popup while the initial one stays open.
The following code is a full working sample to reproduce the behaviour.
<Window x:Class="ToolbarProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350"
Width="525">
<StackPanel>
<ToolBar ItemsSource="{Binding Items}">
<ToolBar.ItemTemplate>
<DataTemplate >
<StackPanel VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleButton Name="ToggleButton"
ToolTip="ToolTip"
IsChecked="{Binding ElementName=ContextActionPopup, Path=IsOpen, Mode=TwoWay}"
ClickMode="Release">
<TextBlock Text="ICON"/>
</ToggleButton>
<Popup Name="ContextActionPopup"
StaysOpen="False">
<Border x:Name="Border"
Background="White"
Padding="1"
Visibility="Visible">
<TextBlock Text="Content" />
</Border>
</Popup>
</StackPanel>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
</StackPanel>
namespace ToolbarProblem
{
using System.Collections.Generic;
public partial class MainWindow
{
public MainWindow()
{
this.InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
public class MainWindowViewModel
{
public List<object> Items { get; } = new List<object>
{
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object(),
new object()
};
}
}
I did tried the following approaches without any success:
Add some code to call Popup.Focus
Change ToggleButton.ClickMode to everything possible
Setting the Popup.StaysOpen property to true does seem to work, but is
of course inappropriate for my target
I am afraid that your XAML will never work properly. You can find the reason in the Popup control code. If you set the StaysOpen property to false, the Popup when opening, will call this private method (just use ILSpy to inspect):
private void EstablishPopupCapture()
{
if (!this._cacheValid[1] && this._popupRoot.Value != null && !this.StaysOpen && Mouse.Captured == null)
{
Mouse.Capture(this._popupRoot.Value, CaptureMode.SubTree);
this._cacheValid[1] = true;
}
}
So if there is no other control that is capturing mouse events (i.e. Mouse.Captured == null) your popup will capture them
to determine when one of these events occurs outside the Popup control.
as MSDN remarks. Please, note that Capture method and Captured properties are static, so just one control at a time can capture by using Mouse class.
Now just take a look to the Toolbar default style: you will find that its Popup control, named "OverflowPopup", has its StaysOpen property set to false.
So when you click on the overflow thumb, the "OverflowPopup" calls the EstablishPopupCapture while it is opening. In this case Mouse.Captured is null so it can capture mouse events.
After a while you click on a ToggleButton which is inside the "OverflowPopup" (so it will continue to stay open). Now your Popup - while opening - calls EstablishPopupCapture, but this time Mouse.Captured is not null. So it is not able to listen to mouse events by using Mouse class.
I guess you should consider changing your XAML or adopting a different solution (for example you can write your own template for your Toolbars in order to set OverflowPopup's StaysOpen property to true).
This is a simplified explanation, but the fact of the matter is that it is not possible to have two or more opened popups with StaysOpen set to false: they simply cannot work as expected. Just try to run this XAML:
<StackPanel Orientation="Horizontal">
<Button Content="I am button 1" Name="btn1" Height="20" />
<Button Content="I am button 2" Name="btn2" Height="20" />
<Popup StaysOpen="False" IsOpen="True" PlacementTarget="{Binding ElementName=btn1}">
<TextBlock Text="Popup1" Margin="6" Background="Red" />
</Popup>
<Popup StaysOpen="False" IsOpen="True" PlacementTarget="{Binding ElementName=btn2}">
<TextBlock Text="Popup2" Margin="6" Background="Red" />
</Popup>
</StackPanel>
And you will see that Popup2 will not work as it should do.
Ultimate Goal
When a user clicks the Expand button, I want the WebContainerControl to be full screen, be focused, not allow scrolling in the ScrollViewer, and overlap the title grid (with the back button, page title, etc.)
Basically, it should be like clicking on a photo in a nice photo viewing app. Exapnd to full screen, have an X button in the top right corner and when you click it, it goes back to the regular view.
Problem
Since it's a WebView, I can't simply pass the view to a popup (It gives me an invalid args exception since the current browsing session can't be passed by reference... they logged in on a site, so it would be insecure I assume)
I have a XAML control with a webview in it:
<UserControl x:Class="App.WebContainerControl">
<Grid x:Name="grdWebContainer">
<StackPanel>
<Button Click="btnExpandView_Click"/>
<WebView x:Name="wvSite"/>
</StackPanel>
</Grid>
</UserControl>
Here is an example view it would be loaded into:
<Grid x:Name="grdMain">
<Grid.RowDefinitions>
<RowDefinition Height="140"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title Grid -->
<Grid x:Name="grdTitleBar" Grid.Row="0">
<TextBlock Text="App Title"/>
</Grid>
<!-- Web Views -->
<Grid Grid.Row="1">
<ScrollViewer>
<StackPanel>
<controls:WebContainerControl x:Name="First Site"/>
<controls:WebContainerControl x:Name="Second Site"/>
</StackPanel>
</ScrollViewer>
</Grid>
</Grid>
What I have so far
So far, when they press the Expand button, it makes the control full screen (using Current.Window.Bounds)
Then, I pass the event that the button is pressed to the main view:
private void OnAccount_Expanded(object sender, ExpandedEventArgs args) {
// Expanded button is pressed and control is made full screen
if (args.IsExpanded) {
// Hide titlebar
grdMain.RowDefinitions[0].Height = GridLength.Auto;
grdTitleBar.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
else {
// show titlebar again
GridLength gl = new GridLength(140);
grdMain.RowDefinitions[0].Height = gl;
grdTitleBar.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
The Question
Right now, it makes it full screen, but I can still scroll. Any idea how to set the ScrollViewer to horizontally center on the control? If anyone has a better idea on how to achieve my Ultimate Goal, you would make me one happy camper! (Remember, it won't allow me to pass my control around, only manipulate it)
If I understand well, Put name on you Scroll Viewer
<ScrollViewer Name="uiScroll" >
............
</ScrollViewer >
When you doing full screen set visibility of you scrollBar
uiScroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
Is there any way to disable background GUI interaction when popup dialog open?
My popup dialog is a UserControl so cannot manually set the content of that page using isEnabled to false property as my popup dismiss login is on that usercontrol page.
Thanks in advance.
You can set a background grid on your user control and on the background grid set IsHitTestVisible="False" Your popover will be defined after the grid so it's placed on top and can receive user input.
To disable the app bar, you can disable that when the popup opens. If there is a different app bar on every page then it's possible you could write a method which would find any app bar in the UI and disable it until the popup window is closed.
WinRTXAMLToolkit has a visual tree helper class, which could be used to find the app bars.
var AppBars = Window.Current.Content.GetDescendentsOfType<type of app bar>();
foreach(var appBar in AppBars)
{
appBar.IsEnabled=false;
}
When the popup window is hidden, re-enable the app bars.
In your event handler (Button) that opens the popup, write the following piece of code.
private void PopupButton_Click(object sender, RoutedEventArgs e)
{
this.IsHitTestVisible = false;
this.Opacity = 0.5;
MyPopup.IsOpen = true;
}
And when your popup closes, you may catch the event and write the following code.
private void MyPopup_Closed(object sender, object e)
{
this.IsHitTestVisible = true;
this.Opacity = 1;
}
This would give the same visual effect that you get when once opens a Dialog.
I have used SwapChainPanel to disable parent view content user interaction. Also I have added gray overlay while my custom popup is shown.
<SwapChainPanel x:Name="DirectXPanel" Visibility="{Binding IsOpen, Converter={StaticResource BooleanToVisibilityConverter}}" >
<Border CornerRadius="5" Opacity="0.60" Background="#000000" ></Border>
</SwapChainPanel>
<UserControls:CustomLoginPanel x:Name="CustomLoginPanel" Height="220" Width="400"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Grid.Row="0"
IsOpen="{Binding IsOpen, Mode=TwoWay}"
Margin="0,70,0,0"/>
Here ProgressDialog is my custom popover which I want to show while user is login.
SwapChainPanel is going to cover entire window while custom popover is shown. By default it is hidden.
IsOpen is property exist in my view. This property is used to Show/Hide the Popover.
"BooleanToVisibilityConverter" is a converter used to convert boolean to Visibility
BooleanToVisibilityConverter.cs:
public sealed class BooleanToVisibilityConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return (value is bool && (bool)value) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return value is Visibility && (Visibility)value == Visibility.Visible;
}
}
A workaround to disable the space that your popup control is not taking could be actually taking that space, and making the "unused" space transparent or partially transparent to get that nice message dialog effect.
For example, if your popup looks like a classic winrt message dialog (full width and vertically centered), make your popup control have the same width and height as your background and make the popup's content a grid with 3 row definitions. Place the actual content in the middle row, so it will be vertically centered. Then place a grid in the first row with black background color and opacity of 0.4, and with a rowspan of 3 so it will take all the space. Once you Open the popup, it will take focus over the whole background so the user cannot interact with it but still being able to see the background that the actual content doesn't overlap.
Here a simple example of what i say:
<UserControl ...>
<Popup x:Name="PopupControl"
IsLightDismissEnabled="False"
Loaded="PopupControl_Loaded">
<!-- The content of the Popup, a grid with 3 rows. The second row takes half of the space from the popup -->
<Grid x:Name="PopupGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Place a grid on the first row with black background, a rowspan of 3 will take all the rows so the user cannot interct with the actual background. -->
<Grid Grid.RowSpan="3"
Background="Black"
Opacity="0.4" />
<!-- Here place the actual content of your popup. -->
<Grid Grid.Row="1" Background="White">
[Acutal content of the popup]
</Grid>
</Grid>
</Popup>
</UserControl>
In the code behind of the user control:
// Finally make the popup control full screen.
private void PopupControl_Loaded(object sender, RoutedEventArgs e)
{
this.PopupGrid.Height = Window.Current.CoreWindow.Bounds.Height;
this.PopupGrid.Width = Window.Current.CoreWindow.Bounds.Width;
}
I am trying to implement a kind of single-line tab control in WPF which shows scroll buttons left and right of the area containing the tabs. The tabs are implemented inside a custom control. The scroll buttons shall be displayed only if the window is too small to display all tabs.
When the window is resized by dragging on its borders, everything works as expected.
But when the window is maximimized and then restored, then the right scroll button remains hidden.
The problem seems to appear only if the Visibility property of the right scroll button is data-bound to a dependency property of the custom control that is updated within the custom control's Measure pass.
My question is: am I using WPF correctly here, or is there something that needs to be done in a different way? (Please note: I need to use the data-binding and the custom control; therefore please avoid answers that suggest to take a completely different approach.)
Here is a small sample program which illustrates the problem:
When width is small:
When width is large:
These are the files of the sample program:
MainWindow.xaml:
<Window x:Class="GridTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:gridTest="clr-namespace:GridTest"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="theGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button x:Name="btnScrollLeft" Content="<" Grid.Row="0" Grid.Column="0" Width="30"/>
<gridTest:MyCustomControl x:Name="cust" Grid.Row="0" Grid.Column="1"/>
<Button x:Name="btnScrollRight" Content=">" Grid.Row="0" Grid.Column="2" Width="30"
Visibility="{Binding ElementName=cust, Path=ShowButton}"/>
<TextBox Text="The content goes here..." Grid.Row="1" Grid.ColumnSpan="3"
Background="LightGreen" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Button x:Name="btnRedraw" Grid.Row="1" Grid.Column="1" Content="Redraw" VerticalAlignment="Bottom"
HorizontalAlignment="Center" Click="btnRedraw_Click" />
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
namespace GridTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnRedraw_Click(object sender, RoutedEventArgs e)
{
theGrid.InvalidateMeasure();
}
}
}
MyCustomControl.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace GridTest
{
public class MyCustomControl : Control
{
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
}
public Visibility ShowButton
{
get { return (Visibility)GetValue(ShowButtonProperty); }
set { SetValue(ShowButtonProperty, value); }
}
// Using a DependencyProperty as the backing store for ShowButton. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ShowButtonProperty =
DependencyProperty.Register("ShowButton", typeof(Visibility), typeof(MyCustomControl), new UIPropertyMetadata(Visibility.Visible));
protected override Size MeasureOverride(Size constraint)
{
if (constraint.Width > 800)
{
ShowButton = Visibility.Collapsed;
}
else
{
ShowButton = Visibility.Visible;
}
double width = Math.Min(2000.0, constraint.Width);
double height = Math.Min(50.0, constraint.Height);
return new Size(width, height);
}
}
}
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:GridTest">
<Style TargetType="{x:Type local:MyCustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Border Background="LightCyan">
<TextBlock VerticalAlignment="Center" TextAlignment="Center">Custom Control</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
The problem can be reproduced as follows:
Make sure that the window is small, such that the right scroll button is visible.
Now maximize the window. => The right scroll button should become invisible.
Now restore the window to its original size. => The right scroll button should become visible again. (The problem is: the right scroll button remains invisible.)
EDIT: Just for information: I could reproduce the problem with both VS2010+.NET4.0 and VS2013+.NET4.51.
You need to dispatch your ShowButton visibility change back into the dispatch queue for this to work(give the application the time it needs for the render and in sequence) rather than do it directly in the MeasureOverride method.
So say I change your MeasureOverride to
protected override Size MeasureOverride(Size constraint) {
if (constraint.Width > 800) {
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ShowButton = Visibility.Collapsed));
} else {
Application.Current.Dispatcher.BeginInvoke(
new Action(() => ShowButton = Visibility.Visible));
}
double width = Math.Min(2000.0, constraint.Width);
double height = Math.Min(50.0, constraint.Height);
return new Size(width, height);
}
You can see it works fine.
With the original code you posted, you can see that even when you maximize your Window, the Button on the right will be hidden but not actually Collapsed like that your setting it to, that would be again due to the same reason of the control not getting it's new size.
Also after restoring and having the Button remain hidden, if you re-size the Window by dragging it's size, you can see the button become Visible again.
By dispatching the Visibility change, if you have a break-point in your MeasureOverride function, you can see it gets called twice(once for Window size change and second size change due to Button being Hidden/Shown) essentially when maximizing/restoring the Window and thereby produce the correct dimension calculations and your desired output.
Having worked with some custom Panels before, I seem to remember that the value passed into the MeasureOverride method is not the actual size used... try moving that code to the ArrangeOverride method instead and see what happens.