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;
Related
I'm trying to change the behaviour when there's on-screen keyboard presence similar to what is described on https://msdn.microsoft.com/en-gb/windows/uwp/input-and-devices/respond-to-the-presence-of-the-touch-keyboard?f=255&MSPPError=-2147217396 but not actually clarified as to how to achieve:
In some cases, there are UI elements that should stay on the screen
the entire time. Design the UI so that the form controls are contained
in a panning region and the important UI elements are static.
This is my page's basic xaml layout:
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid x:Name="titlebar" Grid.Row="0" />
<RichEditBox x:Name="content" Grid.Row="1" />
</Grid>
What I want is for the titlebar element to still be visible on-screen when the touch-screen keyboard is opened. At the moment, what's happening is when the on-screen keyboard is called, the page is automatically scrolled down so that the RichEditBox appears at the very top of my view (requiring me to scroll up in order to get the titlebar back into view).
Any guidance on how to achieve the behaviour I'm after will be much appreciated.
Put both header TextBlock and RichTextBox in a Grid and then use translateY on the Grid's transform when RichTextBox receive focus.
Maybe something like this https://social.msdn.microsoft.com/Forums/windowsapps/en-US/99652e87-113c-47fa-a8e7-60f11fc9f160/virtual-keyboard-covering-textbox
http://blog.jerrynixon.com/2013/08/windows-8-xaml-manipulation-using.html?m=1
I'm using Caliburn.Micro as a MVVM framework and I have an app that has a TabControl and each Tab is a ViewModel (and View) that has a couple of buttons on it and a custom UserControl I built, that also has a button in it. All of the tabs have the same structure (they use the same ViewModel/View).
The problem is that, for some reason, when I click the button inside the custom UserControl, that resets other Tabs - the controls inside reset to their initial values, DataGrids get cleared etc. The weird thing about this is that:
it doesn't happen always, it doesn't always happen to all tabs AND it happens even if I comment out everything within the UserControl's button's Click event (so just by the Click event being raised, some and sometimes all tabs just reset for no reason).
I've read that TabControl has this weird thing where it doesn't persist data in some cases, but
a) I don't think this is the case, because the data persists fine when switching between tabs, it just disappears when I click the button
b) Even if it is the same issue, I can't really use the solutions provided by Google, because the binding of Views, ViewModels and the TabControl is done by Caliburn.Micro and I can't mess around with how it does that (so, for example, I can't make the TabControl use a new property instead of ItemSource as some posts suggest).
It looks like it just completely resets the whole view just as if the app was just launched. When I read about the persistence issues of TabControl, people usually meant that things like sorting settings, selections get cleared, but in this case the whole tab clears including the data of DataGrids and everything else. I noticed that it only re-creates the views (their constructors get called when switching back to their tabs), but the ViewModels behind the views don't!
Has anyone else experienced this before? What did you do?
I had been searching for hours and somehow completely missed this solution: Stop TabControl from recreating its children
I'm not really sure how it works, but it somehow stops the Views from getting re-created when switching tabs and pressing any buttons.
One Solution maybe to avoid using TabItems to hold your controls. Instead leave the TabItems empty, and add all of the controls that normally would go in a TabItems into the same grid element and set the Panel.ZIndex higher for the control that you want on top. Example:
<Window x:Class="testtab.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="testtab" Height="300" Width="300"
>
<Grid Name="Grid1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Persistant State for UserControls"
Background="Blue" Foreground="Yellow"/>
<TabControl Grid.Row="1"
Name="TabControl1"
SelectionChanged="TabControl_SelectionChanged">
<TabItem Header="Page1" />
<TabItem Header="Page2" />
<TabItem Header="Page3" />
</TabControl>
<!-- ZIndex: top=1; botton=0 -->
<TextBox Name="b1" Grid.Row="2" Panel.ZIndex="1" Text="b1"/>
<TextBox Name="b2" Grid.Row="2" Panel.ZIndex="0" Text="b2"/>
<TextBox Name="b3" Grid.Row="2" Panel.ZIndex="0" Text="b3"/>
</Grid>
</Window>
Here's the related event handler:
void TabControl_SelectionChanged(
object sender,
SelectionChangedEventArgs e)
{
//need this if settings SelectedIndex on TabControl
if (!IsInitialized) return;
switch(TabControl1.SelectedIndex) {
case 0:
Panel.SetZIndex(b1, 1);
Panel.SetZIndex(b2, 0);
Panel.SetZIndex(b3, 0);
break;
case 1:
Panel.SetZIndex(b1, 0);
Panel.SetZIndex(b2, 1);
Panel.SetZIndex(b3, 0);
break;
case 2:
Panel.SetZIndex(b1, 0);
Panel.SetZIndex(b2, 0);
Panel.SetZIndex(b3, 1);
break;
}
e.Handled = true;
}
I don't have any UserControls handy... so We are using TextBox controls...
One Afterthought, You might need too also control the Visibility property for each control selecting between "Collapsed" and "Visible". In other words, if the control is not shown because its tab is not selected, then its Visibility should be set to collapsed or hidden so as not to interfere with the tab that is on top.
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 new for WPF so apologies if the answer is so obvious.
I got a WPF resizable window with a single stack panel control that is stretched vertically and horizontally to fill the window.
On window activated event, I use ".Children.Add" to add button controls to the panel. I have no idea how many buttons will be there at runtime so I checked "CanVerticallyScroll" in the panel. ScrollViewer.VerticalScrollBarVisibility is set to Visible by default.
I am still not seeing scroll bars at runtime though.
What properties did I miss to show scrolling panel with buttons?
Thanks
XAML:
<Window x:Class="ResMed.Ecp.Utility.ConnectionWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ConnectionWindow" Height="388" Width="641.6" Activated="Window_Activated">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="359*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="pnlConnectionButtons" Margin="10,10.2,10.2,10" Grid.Row="1" CanVerticallyScroll="True"/>
</Grid>
</Window>
Code behind:
private void Window_Activated(object sender, EventArgs e)
{
for (int i = 0; i < 20; i++)
{
Button btn = new Button();
btn.Content = "Hello";
pnlConnectionButtons.Children.Add(btn);
}
}
Place your StackPanel inside a ScrollViewer:
<ScrollViewer>
<StackPanel>
<Button Content="Hello World"></Button>
...
...
</StackPanel>
</ScrollViewer>
You can also remove CanVerticallyScroll="True". From MSDN:
This property is not intended for use in your code. It is exposed publicly to fulfill an interface contract (IScrollInfo). Setting this property has no effect.
We are using the prism and WPF to build application. Recently we started using UI Automation (UIA) to test our app. But some strange behavior occurred when we run UIA test. Here's simplified shell:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock
Grid.Row="0" Grid.Column="0"
Name="loadingProgressText"
VerticalAlignment="Center" HorizontalAlignment="Center"
Text="Loading, please wait..."/>
<Border
Grid.Row="0"
x:Name="MainViewArea">
<Grid>
...
</Grid>
</Border>
<!-- Popup -->
<ContentControl
x:Name="PopupContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="PopupRegion"
Focusable="False">
</ContentControl>
<!-- ErrorPopup -->
<ContentControl
x:Name="ErrorContentControl"
Grid.Row="0"
prism:RegionManager.RegionName="ErrorRegion"
Focusable="False">
</ContentControl>
</Grid>
In our app, we use layers (Popup and ErrorPopup) to hide MainViewArea, to deny access to the controls. To show Popup, we use next method:
//In constructor of current ViewModel we store _popupRegion instance to the local variable:
_popupRegion = _regionManager.Regions["PopupRegion"];
//---
private readonly Stack<UserControl> _popups = new Stack<UserControl>();
public void ShowPopup(UserControl popup)
{
_popups.Push(popup);
_popupRegion.Add(PopupView);
_popupRegion.Activate(PopupView);
}
public UserControl PopupView
{
get
{
if (_popups.Any())
return _popups.Peek();
return null;
}
}
Similar to this, we show ErrorPopup over all elements of our application:
// In constructor we store _errorRegion:
_errorRegion = _regionManager.Regions["ErrorRegion"]
// ---
private UserControl _error_popup;
public void ShowError(UserControl popup)
{
if (_error_popup == null)
{
_error_popup = popup;
_errorRegion.Add(_error_popup);
_errorRegion.Activate(_error_popup);
}
}
Mistics...
When we run it as users do it (double click on app icon), we can see both custom controls (using AutomationElement.FindFirst method, or through Visual UI Automation Verify). But when we start it using UI Automation test - ErrorPopup disapears from the tree of the controls. We trying to start the application like this:
System.Diagnostics.Process.Start(pathToExeFile);
I think that we missed something. But what?
Edit #1
As #chrismead said, we tried to run our app with UseShellExecute flag set to true, but this does not help. But if we start app from cmd line, and manually click the button, Popup and ErrorPopup are visible in automation controls tree.
Thread appThread = new Thread(delegate()
{
_userAppProcess = new Process();
_userAppProcess.StartInfo.FileName = pathToExeFile;
_userAppProcess.StartInfo.WorkingDirectory = System.IO.Directory.GetCurrentDirectory();
_userAppProcess.StartInfo.UseShellExecute = true;
_userAppProcess.Start();
});
appThread.SetApartmentState(ApartmentState.STA);
appThread.Start();
One of our suggestion is when we use method FindAll or FindFirst to search the button to click, window somehow cached its UI Automation state, and does not update it.
Edit #2
We have find, that extension method of prism library IRegionManager.RegisterViewWithRegion(RegionNames.OurRegion, typeof(Views.OurView)) have some strange behavior. If we stopped use it, this solve our problem particulary. Now we able to see ErrorView and any kind of view in PopupContentControl, and application updates UIA elements tree structure. But this is not an answer - "Just stop use this feature"!
In MainViewArea we have a ContentControl, which updates it content depending on user actions, and we are able to see only the first loaded UserControl to that ContentControl.Content property. This is performed like this:
IRegionManager regionManager = Container.Resolve<IRegionManager>();
regionManager.RequestNavigate(RegionNames.MainContentRegion, this.Uri);
And if we change the view, no updates will performed in UI Automation tree - the first loaded view will be in it instead. But visually we observe another View, and WPFInspector shows it properly (its show not a UI Automation tree), but Inspect.exe - not.
Also our suggestion that window use some kind of caching is wrong - caching in UI Automation client we have to turn on explicitly, but we don't do it.
I'm sorry that I've missed some detail, that was the key to the answer. I think that it was not important thing. Anyway.
We used NavBar from DevExpress controls library for WPF. What turns out, is when NavBar is present, dynamically created views are not appears on the UI Automation tree. When remove it from the window, there was an ability to see all dynamically loaded views. What does the NavBar - still mistic for me.
Here bright example to see what happened, if NavBar is present or absent on the Window (DevExpress is required).
MainWindow.xaml:
<Window xmlns:dxn="http://schemas.devexpress.com/winfx/2008/xaml/navbar"
x:Class="Test.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 Name="ContentGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<!--Comment NavBar to see dynamic control in UI Automation tree-->
<dxn:NavBarControl Name="asdasd">
<dxn:NavBarControl.Groups>
<dxn:NavBarGroup Header="asdasdasdasd" />
</dxn:NavBarControl.Groups>
</dxn:NavBarControl>
<TextBox Grid.Column="1" Name="Statictb" Text="static is visible in ui automation tree" />
<Button Grid.Row="1" Content="Create controls" Height="25" Click="Button_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
TextBox tb = new TextBox();
Grid.SetRow(tb, 1);
Grid.SetColumn(tb, 1);
tb.Text = "dynamic is not visible, if NavBar here...";
ContentGrid.Children.Add(tb);
}
}
Edit
According to the DevExpress answer on their support site:
After a peer is created, listening of automation events may cause performance issues. We have decided to clear invocation lists of automation events to resolve it. In your specific situation, you need to disabling clearing. To do it, please set the static DevExpress.Xpf.Core.ClearAutomationEventsHelper.IsEnabled property to False in the Window constructor.
This solve the problem.
My guess is that the ContentControl's automation peer should update its children with AutomationPeer.ResetChildrenCache() after the view has been changed.
AutomationPeer.InvalidatePeer() should have the same effect (in addition to other side effects) and it is supposed to be called automatically in response to the LayoutUpdated event. You might want to check that the LayoutUpdated event is raised when the view changes.
stukselbax, try to find a sequence of keystrokes (TABs, and an ENTER most likely) to click the button that enables you to see the items. it is pretty easy to send keystrokes and i can add more in here about that if that works for you. you can always establish a tab order in your application that makes the most sense for users.
------ Update on 6/20/12 --------
Have you tried double clicking a shortcut to your app on the desktop using PInvoke to see if you can see the controls when it is opened that way? Here is a link to an example here on stackoverflow:
Directing mouse events [DllImport("user32.dll")] click, double click
Another idea: some of the controls on the app I am currently automating don't show up in the tree until a mouse click occurs on them. To accomplish this without using any hardcoded coordinates, I find something in the tree which is just (above/below/etc) the place where I need to click to get the control to appear. I then get the mouse coordinates for that item and put the mouse at a small offset from there and click. Then I can find my controls in the tree. If the app is resized, moved around, etc. this will still work since the small offset is still valid.