Hide/Display XAML elements or block LostFocus event - c#

I have a page containing two StackPanels, each containing one TextBox and one Button:
<StackPanel x:Name="Row1">
<TextBox x:Name="TextBox1" Text="" GotFocus="OnFocusHandler" LostFocus="OffFocusHandler"/>
<Button x:Name="Button1" Content="Convert" Click="OnClickHandler" Visibility="Collapsed"/>
</StackPanel>
<StackPanel x:Name="Row2">
<TextBox x:Name="TextBox2" Text="" GotFocus="OnFocusHandler" LostFocus="OffFocusHandler"/>
<Button x:Name="Button2" Content="Convert" Click="OnClickHandler" Visibility="Collapsed"/>
</StackPanel>
I would like to do the following:
When a textbox has focus, the other textbox must be hidden and the corresponding button must show
When a textbox is out of focus, we are back to the original display: only empty textboxes are visible
I don't want the button to be able to trigger the OffFocusHandler
This is the current code that I have for the three handlers:
private void OnFocusHandler(object sender, RoutedEventArgs e)
{
TextBox SenderTextBox = (TextBox)sender;
if (SenderPanel.Name == "TextBox1")
{
Button1.Visibility = Visibility.Visible;
}
else if (SenderPanel.Name == "TextBox2")
{
Button2.Visibility = Visibility.Visible;
}
}
private void OffFocusHandler(object sender, RoutedEventArgs e)
{
TextBox1.Text = "";
TextBox2.Text = "";
Button1.Visibility = Visibility.Collapsed;
Button2.Visibility = Visibility.Collapsed;
}
private void OnClickHandler(object sender, RoutedEventArgs e)
{
// some stuff unrelated to my issue
}
How do I avoid the button clicking to trigger the OffFocusHandler code?
Is there another way to code this? I'm a complete beginner so I may not think the right way.

You can just Bind to the TextBox.IsFocused property in Xaml, and use the BooleanToVisibilityConverter to show/hide the button.
Example:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication4"
Title="MainWindow" Height="300" Width="400" Name="UI" >
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolTovisible" />
</Window.Resources>
<Grid>
<StackPanel x:Name="Row1" Height="54" VerticalAlignment="Top">
<TextBox x:Name="TextBox1" Text="" />
<Button x:Name="Button1" Content="Convert" Visibility="{Binding ElementName=TextBox1, Path=IsFocused, Converter={StaticResource BoolTovisible}}"/>
</StackPanel>
<StackPanel x:Name="Row2" Margin="0,60,0,0" Height="51" VerticalAlignment="Top">
<TextBox x:Name="TextBox2" Text="" />
<Button x:Name="Button2" Content="Convert" Visibility="{Binding ElementName=TextBox2, Path=IsFocused, Converter={StaticResource BoolTovisible}}"/>
</StackPanel>
</Grid>
</Window>

for each element, there is a Visibility tag, it is "Visible" by default but you can assign "Hidden" or "Collapsed" as follow:
<RadioButton Margin="20,118,318,-43" GroupName="MCSites" Visibility="Hidden">
Radio Button Description
</RadioButton>

Related

WPF PlacementTarget for PopUp at UserControl TextBox

I would like to have a popup show at the bottom of each textbox in my window, as they are focused.
The user would be presented with the last few entries entered in that textbox. I would like the placement to be such that it would be at the bottom of the textbox currently focused.
This is my user control with the textbox:
<UserControl x:Class="PopupPlacement.MyControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox Name="TextBox_MyControl" Text="enter your text here" Height="25" Width="200"/>
</StackPanel>
</UserControl>
Here is my window:
<Window x:Class="PopupPlacement.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PopupPlacement"
Title="MainWindow" Height="450" Width="800">
<Canvas>
<Grid ShowGridLines="False">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Domain" Margin="10"/>
<local:MyControl Grid.Column="1" x:Name="Domain" Margin="10"/>
<Label Grid.Row="1" Content="Username" Margin="10"/>
<local:MyControl Grid.Row="1" Grid.Column="1" x:Name="Username" Margin="10"/>
<Label Grid.Row="2" Content="Password" Margin="10"/>
<local:MyControl Grid.Row="2" Grid.Column="1" x:Name="Password" Margin="10"/>
<Button Grid.Row="3" Content="OK" Margin="10" Name="Button_OK"/>
<Button Grid.Row="3" Grid.Column="1" Content="Cancel" Margin="10"/>
<Popup PlacementTarget="{Binding ElementName=TextBox_MyControl}" Placement="Bottom"
IsOpen="{Binding ElementName=TextBox_MyControl, Path=IsKeyboardFocused}">
<ComboBox IsDropDownOpen="True">
<ComboBoxItem IsSelected="True">Item 1</ComboBoxItem>
<ComboBoxItem>Item 2</ComboBoxItem>
</ComboBox>
</Popup>
</Grid>
</Canvas>
</Window>
Appreciate any pointers.
For me, the best solution to a similar requirement was to write a Behavior that kind of mimics Intellisense.
I don't have any simple code at hand, but you could create and show a ListBox inside a Popup placed at the AssociatedObject's bottom. You can then bind the TextBox-related entries to the Behavior via a DependencyProperty.
Of course, there's a lot more to it like closing the Popup, re-using existing controls, handling key presses to access the ListBox, insert the selected value to the TextBox etc.
Here's a simple (untested) sketch.
public class IntellisenseBehavior : Behavior<TextBox>
{
public IEnumerable ItemsSource
{
get => (IEnumerable)GetValue(ItemsSourceProperty);
set => SetValue(ItemsSourceProperty, value);
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(IntellisenseBehavior), new UIPropertyMetadata(null));
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotKeyboardFocus += AssociatedObjectOnGotKeyboardFocus;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.GotKeyboardFocus -= AssociatedObjectOnGotKeyboardFocus;
//cleanup
}
private void AssociatedObjectOnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var popup = new Popup
{
ClipToBounds = false,
AllowsTransparency = true,
PopupAnimation = PopupAnimation.Fade,
HorizontalAlignment = HorizontalAlignment.Left
};
popup.SetValue(FocusManager.IsFocusScopeProperty, true);
popup.Placement = PlacementMode.Bottom;
popup.PlacementTarget = AssociatedObject;
var shadow = new SystemDropShadowChrome { Color = Colors.Transparent, MaxHeight = 200, Margin = new Thickness(0, 0, 5, 5) };
var listBox = new ListBox
{
ItemsSource = ItemsSource
}
((IAddChild)shadow).AddChild(listBox);
((IAddChild)popup).AddChild(shadow);
popup.IsOpen = true;
}
}
Attach it to all TextBoxes that you require to have this functionality and for instance use a converter to get the filtered entries you need.
<!-- Uses converter's public const string NameBox = "NameBox"; for filtering. -->
<TextBox>
<i:Interaction.Behaviors>
<IntellisenseBehavior ItemsSource="{Binding LastEntries, Converter={StaticResource FilterEntriesConverter}, ConverterParameter={x:Static FilterEntriesConverter.NameBox}}" />
</i:Interaction.Behaviors>
</TextBox>
Hope that helps.

UWP Navigation Pane go back button not working

I'm learning UWP and trying to implement GO BACK button in a navigation pane. I put go-back button under a RelativePanel right below menu button. The below is my current XAML page:
<Page
x:Class="LearningUWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:LearningUWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:muxc="using:Windows.UI.Xaml.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<RelativePanel>
<Button Name="Menu" FontFamily="Segoe MDL2 Assets" Content="" FontSize="36" Click="Menu_Click"></Button>
<Button RelativePanel.Below="Menu" Style="{StaticResource NavigationBackButtonNormalStyle}" Name="Back" FontSize="36" Click="Back_Click"></Button>
</RelativePanel>
<SplitView Name="MySplitView"
Grid.Row="1"
DisplayMode="CompactOverlay"
OpenPaneLength="200"
CompactPaneLength="56"
HorizontalAlignment="Left">
<SplitView.Pane>
<ListBox SelectionMode="Single"
Name="IconsListBox"
SelectionChanged="IconsListBox_SelectionChanged"
>
<ListBoxItem Name="ShareListBoxItem">
<StackPanel Orientation="Horizontal" >
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="36" Text=""/>
<TextBlock Text="Share" FontSize="24" Margin="20, 0, 0, 0"/>
</StackPanel>
</ListBoxItem>
<ListBoxItem Name="FavoritesListBoxItem" >
<StackPanel Orientation="Horizontal" >
<TextBlock FontFamily="Segoe MDL2 Assets" FontSize="36" Text=""/>
<TextBlock Text="Favorites" FontSize="24" Margin="20, 0, 0, 0"/>
</StackPanel>
</ListBoxItem>
</ListBox>
</SplitView.Pane>
<SplitView.Content>
<TextBlock Margin="50, 0, 0, 0" Name="ResultTextBlock"/>
</SplitView.Content>
</SplitView>
</Grid>
</Page>
And the XAML's code-behind:
namespace LearningUWP
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
private void Menu_Click(object sender, RoutedEventArgs e)
{
MySplitView.IsPaneOpen = !MySplitView.IsPaneOpen;
}
private void IconsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ShareListBoxItem.IsSelected)
ResultTextBlock.Text = "shared";
else if (FavoritesListBoxItem.IsSelected)
ResultTextBlock.Text = "Favorites";
}
private void Back_Click(object sender, RoutedEventArgs e)
{
if (this.Frame.CanGoBack)
this.Frame.GoBack();
}}}
For some reason, after I click the Go back button, it doesn't work as expected, and what is more, I find this.Frame.CanGoBack = false.
How to solve it?
From the code that you have posted we can see that this.Frame is actually the refering to the root frame of the application, which at the moment has only navigated to a single page (MainPage) (As defined in your App.xaml.cs). Thus there is no page that it can go back to (this.Frame.CanGoBack = false).
A little in depth explanation :
If you go into App.xaml.cs file in your project, in the OnLaunched() method you will find the following code :
rootFrame.Navigate(typeof(MainPage), e.Arguments);
Here the application, after launch will navigate the rootFrame to the MainPage.
When you use this.Frame from your MainPage it actually refers to the rootFrame, which at this moment has only navigated to the MainPage, thus it does not have any page that it can go back to , hence this.Frame.CanGoBack = false.
Solution :
When you use a SplitView, in the content you should specify a Frame which you can use to navigate between different pages . Thus your app will look something like this :
Here Red rectangle is used to show the rootFrame where as Blue is used to show the Frame which you have to define in your SplitView content.
For this, you need to make minor modifications to your code something like this :
XAML
<Page
.....
.....
<SplitView Name="MySplitView"
.....>
<SplitView.Pane>
.....
</SplitView.Pane>
<SplitView.Content>
<Frame x:Name="appFrame"></Frame>
</SplitView.Content>
</SplitView>
</Page>
C#
private void IconsListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ShareListBoxItem.IsSelected)
appFrame.Navigate(typeof(page1));//navigating to page1
else if (FavoritesListBoxItem.IsSelected)
appFrame.Navigate(typeof(page2));//navigating to page2
}
private void Back_Click(object sender, RoutedEventArgs e)
{
if (appFrame.CanGoBack)
appFrame.GoBack();
}
Hope this helps..!

Set Progress bar visibility in button click inside the datatemplate

I am trying to set the progress bar visibilty in the button click, where both are inside the data template. I can't set the name for the progress bar and sent the visibility, since it is in a template.
Is there any method to achieve this. Following code is what I have tried.
<DataTemplate>
<Grid>
<Button Click="Image_Download" Loaded="Button_Loaded" Tag="{Binding .}" Width="80" Height="80" >
<Button.Template>
<ControlTemplate TargetType="Button">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" >
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Button.Template>
<Image Source="c_image.png" Tag="{Binding .}" />
</Button>
<ProgressBar VerticalAlignment="Bottom" IsIndeterminate="true" Visibility="Collapsed" Style="{StaticResource CustomIndeterminateProgressBar}" />
</Grid>
</DataTemplate>
You can do this
private void Image_Download(object sender, RoutedEventArgs e) {
Button button = sender as Button;
if (button != null) {
Grid grid = button.Parent as Grid;
if (grid != null) {
ProgressBar progressBar = grid.Children.OfType<ProgressBar>().FirstOrDefault();
if (progressBar != null) {
progressBar.Visibility = Visibility.Hidden;
}
}
}
}

creating multiple panels and displaying one panel on button click in wpf

I am new to WPF and I want to create a WPF application with 5buttons. On the click of each button I want a content to be displayed on another panel. Right now I just want different images to be displayed on my right side panel on the button clicks.
Here's my XAML code:
<Window x:Class="GridButton.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyFirstApp" Height="350" Width="525" Loaded="Window_Loaded">
<Viewbox Stretch="Fill" StretchDirection="Both">
<DockPanel>
<StackPanel DockPanel.Dock="left" Margin="5" Width="Auto" VerticalAlignment="Center" Height="Auto">
<Button Content="1" Name="button2" Click="button2_Click">
</Button>
<Button Content="2" Name="button1" Click="button1_Click_1">
</Button>
<Button Content="3" Name="button3" Click="button3_Click">
</Button>
<Button Content="4" Name="button4" Margin="5">
</Button>
<Button Content="5" Name="button5" Margin="5" Click="button5_Click_1">
</Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right">
<Image Name="img1" Source="Blue Hills.jpg" Stretch="Uniform" Visibility="Hidden" ImageFailed="Image_ImageFailed" Height="257" />
</StackPanel>
</DockPanel>
And my xaml.cs file contains code to display image:
private void button2_Click(object sender, RoutedEventArgs e)
{
img1.Visibility = Visibility.Visible;
}
I could get only this far.
You can set the Source property of the Image control in code:
private void buttonx_Click(object sender, RoutedEventArgs e)
{
string path = ... // path to image file here
img1.Source = new BitmapImage(new Uri(path));
}
You could easily reuse the same Click handler for all Buttons and check which one was pressed:
private void Button_Click(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
string path = null;
if (button == button1)
{
path = ... // path to image file 1 here
}
else if ...
if (path != null)
{
img1.Source = new BitmapImage(new Uri(path));
}
}
If you want to remove the a child Panel (or other control) from a parent Panel and add another one, you would have to modify the Panel's Children property:
<StackPanel Name="parent">
<StackPanel Name="child" />
</StackPanel>
parent.Children.Remove(child);
parent.Children.Add(...); // some other control here
This approach would usually make sense if you wanted to create child panels dynamically. If you want to declare everything in XAML you may put all child panels in a Grid and change their visibility as you did already.
However, you might also change the ZIndex attached property.
<Grid>
<StackPanel Name="child1">
</StackPanel>
<StackPanel Name="child2">
</StackPanel>
<StackPanel Name="child3">
</StackPanel>
</Grid>
child3 is topmost by default, but now you can set ZIndex to some value > 0 to make another child topmost:
private void Button_Click(object sender, RoutedEventArgs e)
{
...
// reset ZIndex on previous topmost panel to 0 before
Panel.SetZIndex(child1, 1);
}
Or completely omit the Button/Grid/Panel design and use a TabControl.

webbrowser not navigating if you're not looking at it

I got a strange problem. I have a tabcontrol and 3 tabs. On every tab i got a webbrowser control on it. They all navigate to a website. But it only navigates if you're actually looking at the webbrowser control. So having it minimized on taskbar or systray, wont make it navigate to a website.
Why is that? How can i change this behavior?
[EDIT]
This only seems to happen when i startup the app. After it got 'focus' or a 'look at', this doesn't happen anymore.
Some more info, the navigating happens from a different thread than the UI-thread.
[/EDIT]
[3nd EDIT]
Here is a test case:
XAML code:
<Window x:Class="WPFWebbrowserFocusTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="453" Width="755">
<Grid>
<TabControl Height="390" HorizontalAlignment="Left" Margin="12,12,0,0" Name="tabControl1" VerticalAlignment="Top" Width="709">
<TabItem Header="tabItem1" Name="tabItem1">
<Grid>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="18,17,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
</Grid>
</TabItem>
<TabItem Header="tabItem2" Name="tabItem2">
<Grid>
<WebBrowser Height="352" HorizontalAlignment="Left" Margin="0,6,0,0" Name="webBrowser1" VerticalAlignment="Top" Width="693" Navigated="webbrowser_Navigated" LoadCompleted="webbrowser_LoadCompleted" />
</Grid>
</TabItem>
<TabItem Header="tabItem3" Name="tabItem3">
<Grid>
<WebBrowser Height="346" HorizontalAlignment="Left" Margin="6,6,0,0" Name="webBrowser2" VerticalAlignment="Top" Width="687" Navigated="webbrowser_Navigated" LoadCompleted="webbrowser_LoadCompleted" />
</Grid>
</TabItem>
<TabItem Header="tabItem4" Name="tabItem4">
<Grid>
<WebBrowser Height="346" HorizontalAlignment="Left" Margin="10,10,0,0" Name="webBrowser3" VerticalAlignment="Top" Width="687" Navigated="webbrowser_Navigated" LoadCompleted="webbrowser_LoadCompleted" />
</Grid>
</TabItem>
<TabItem Header="tabItem5" Name="tabItem5">
<Grid>
<WebBrowser Height="346" HorizontalAlignment="Left" Margin="10,10,0,0" Name="webBrowser4" VerticalAlignment="Top" Width="687" Navigated="webbrowser_Navigated" LoadCompleted="webbrowser_LoadCompleted" />
</Grid>
</TabItem>
</TabControl>
</Grid>
Here is the code behind file:
public MainWindow()
{
InitializeComponent();
}
private void webbrowser_Navigated(object sender, NavigationEventArgs e)
{
this.SuppressScriptErrors((WebBrowser)sender, true);
}
private void webbrowser_LoadCompleted(object sender, NavigationEventArgs e)
{
WebBrowser wb = (WebBrowser)sender;
if (e.Uri.AbsoluteUri != wb.Source.AbsoluteUri)
return;
}
public void SuppressScriptErrors(System.Windows.Controls.WebBrowser wb, bool Hide)
{
FieldInfo fi = typeof(System.Windows.Controls.WebBrowser).GetField(
"_axIWebBrowser2", BindingFlags.Instance | BindingFlags.NonPublic);
if (fi != null)
{
object browser = fi.GetValue(wb);
if (browser != null)
{
browser.GetType().InvokeMember("Silent", BindingFlags.SetProperty, null, browser, new object[] { Hide });
}
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
this.webBrowser1.Navigate("http://www.google.com");
this.webBrowser2.Navigate("http://www.google.com");
this.webBrowser3.Navigate("http://www.google.com");
this.webBrowser4.Navigate("http://www.google.com");
}
How to reproduce:
Put a breakpoint inside webbrowser_LoadCompleted. Then press the button which is located on the first tabpage of the tabcontrol.
Dont go to the next tabpage yet, wait a coupled of seconds, like 15 or so.
Then go to tabitem2 or 3/4/5. You'll see that the page just got loaded and the webbrowser_LoadCompleted event got fired.
Here's a code fragment in WPF that works. Once you click the button, it minimizes the application, and after 2 seconds calls navigate to all browsers while the window is minimized. Pages are loaded in all tabs regardless of window state or tab focus.
Make sure are calling Navigate inside a Dispatcher.Invoke. You can't make UI changes in WPF from a different thread unless you call the dispatcher. That might be a problem.
My example below calls the navigation from a different thread.
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525"
StateChanged="Window_StateChanged">
<Grid>
<TabControl Height="225" HorizontalAlignment="Left" Margin="12,12,0,0" Name="tabControl1" VerticalAlignment="Top" Width="491">
<TabItem Header="tabItem1">
<WebBrowser Height="189" Name="webBrowser1" Width="479" />
</TabItem>
<TabItem Header="tabItem2">
<WebBrowser Height="185" Name="webBrowser2" Width="466" />
</TabItem>
<TabItem Header="tabItem3">
<WebBrowser Height="187" Name="webBrowser3" Width="434" />
</TabItem>
</TabControl>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="116,268,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="236,268,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
private void button1_Click(object sender, RoutedEventArgs e)
{
this.WindowState = System.Windows.WindowState.Minimized;
}
private void Window_StateChanged(object sender, EventArgs e)
{
if (this.WindowState == System.Windows.WindowState.Minimized)
{
new Thread((state) =>
{
Thread.Sleep(TimeSpan.FromSeconds(2));
this.Dispatcher.Invoke(new Action(() =>
{
webBrowser1.Navigate(textBox1.Text);
webBrowser2.Navigate(textBox1.Text);
webBrowser3.Navigate(textBox1.Text);
}), null);
}).Start();
}
}
It seems that this is the behavior of this control in WPF according to the docs.

Categories