WPF Usercontrol based on Textbox/Popup definition - wpf

I created a "UserControl" based on a TextBox. That means I created a new UserControl and replaced UserControl by TextBox in xaml and xaml.cs files.
Now I want that my new TextBox control shall have a popup to display some suggestions.
Now my question is: Where can I define the look/structure of the Popup as XAML? The Popup definition shall be part of the newTextBox.
That's what I have:
<TextBox x:Class="WpfApplication11.UserControl2"
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:WpfApplication11"
mc:Ignorable="d"
MaxWidth="{Binding Path=ActualWidth}">
</TextBox>

I think you're totally missing what a UserControl actually is. To put it simply, a UserControl is a group or collection of controls that make up one larger... uh... control.
I want that my new TextBox control shall have a popup to display some suggestions.
What you are describing here is a UserControl. You cannot place a Popup inside a TextBox.
So. What you're after here is probably something like this:
<UserControl ... >
<Grid>
<TextBox Name="txt"
Width="150" ... />
<Popup PlacementTarget="{Binding ElementName=txt}"
Placement="Bottom"
IsOpen="True"
StaysOpen="True"
Width="{Binding ActualWidth, ElementName=txt}">
<!-- Some popup content here -->
</Popup>
</Grid>
</UserControl>
This is obviously an extremely simplified example, but you get the idea.

Related

WPF Popup placement relative to main window

I have a WPF application that uses one window as main window. Now I want to display various popups centered in the window. This works fine when the popup is placed inside the window. However, I have implemented the popups as user control; meaning that the main window contains a user controls which itself contains the actual popup. So the UI element tree looks like this:
Window --> UserControlPopup --> Popup
The popup inside the user control is declared like this:
<UserControl x:Class="X.Custom_Controls.ErrorPopup"
...
xmlns:local="clr-namespace:X.Custom_Controls"
mc:Ignorable="d" d:DesignHeight="360" d:DesignWidth="500">
<Popup Name="errorPopup" Height="360" Width="500" Placement="Center" StaysOpen="True" PlacementTarget="{Binding ElementName=mainWindow}">
...
</Popup> </UserControl>
The mainWindow element name is the one of my main window, which is declared as follows:
<Window x:Class="X.MainWindow" x:Name="mainWindow" ...>
The problem is that the popup is not placed in the center, but on the left side. So my guess is that the popup cannot resolve the elementName properly (since it's a child of a child of the mainWindow). Does anyone know how I can solve this issue in XAML?
Update: the solution was to use
PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
Try to acces your main window through x:Reference. This one should work:
<MainWindow x:Name="mainWindow">
<UserControl Tag="{x:Reference mainWindow}">
</MainWindow>
<UserControl x:Name="userControl">
<PopUp PlacementTarget="{Binding Path=Tag, ElementName=userControl}"/>
</UserControl>
Such a way you can use your UserControl in another Controls/UserControls and you will not need to modify it, just set the Tag property from outside, if you use {RelativeSource AncestorType=Window} you will need to modify it.
The solution was to use the following as PlacementTarget:
PlacementTarget="{Binding RelativeSource={RelativeSource AncestorType=Window}}"

WPF: Textblock text in Popup goes outside of the main window

In my example wpf app I've added one button and one popup to the window. The button is in the bottom right corner and the popup has set "PlacementTarget" property to it and "Placement" set to top. The popup consists of one very long textblock.
What I expect this popup will behave is not to go outside of the window and therefore automatically set his "HorizontalOffset" to the appropriate value, but the popup behaves against my intentions.
Here's my xaml file:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1" x:Name="window" x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:Converters x:Key="Converters"/>
</Window.Resources>
<Grid>
<Button x:Name="button" Content="Button" VerticalAlignment="Bottom" Width="75" HorizontalAlignment="Right"/>
<Popup Placement="Top" PlacementTarget="{Binding ElementName=button, Mode=OneWay}" IsOpen="True">
<TextBlock TextWrapping="Wrap" Text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" Background="White"/>
</Popup>
</Grid>
Do anyone know how to fix it?
I've read that this should be default popup behavior to take care of going out of the boundaries, but not in my case. Thanks in advance.
Have you tried to set the width of the Popup or Textblock ?
Sorry, I can't write this poor answer as a comment..

WPF Expander in user control getting rendered transparent in ListBox item

I’m having issues with a WPF Expander that I have in a user control that gets rendered in a ListBox. Essentially I’m trying to get PopUpButton behavior on each ListItem in my ListBox. When I open the Expander the content is rendering behind everything else as if it were transparent, or lower in the z-order. I’ve tried this with a WPF PopUp and Toggle Button as well (using techniques described int Karle Shivllet’s blog – Expander Control with Popup Content) to no avail.
Let me first describe what it is I’m trying to do. I have two controls that display a list of inputs that I need to configure for my application. For simplicity sake, one user control is used to configure inputs to a graph, and another control is used to control inputs to a simple excel grid. The inputs for the graph and grid each have properties that need to be configured on them. I’ve developed a simple user control called InputSelectControl that will render a ListBox containing the list of inputs to be configured for the graph or grid. Each ListItem in the ListBox consist of a TextBlock for the input’s name (e.g. Pressure, ECG, etc.) and a WPF Expander that , when clicke, displays a property editor for that input. Since the property editor presentation will be different depending on whether I’m dealing with graph inputs versus grid inputs, I’ve used a DependencyProperty on my InputSelectControl that is of type ControlTemplate. This allows my grid and graph to each supply the presentation they need for editing their input properties. Also note that I will have more than just a graph and a grid that need this behavior, thus the desire to make this a user control that can dynamically receive presentation behavior.
I’ve tried placing my Expander inside my property editor template, had have also tried experimenting with the ZIndex in various places, but always end up with the same behavior, the Expander popup displays behind the ListItems in my list.
Below is some code further describing my approach. Hopefully someone can help me out of this pickle.
XAML representing my Grid (could be graph, or something else) control that hold my InputSelectControl:
<UserControl x:Class="MyApp.GridView"
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:props="clr-namespace:PopupButtonDependencyProp" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<!-- Specify the control tempalte we want loaded into the
properies popup for a grid-->
<ControlTemplate x:Key="GridPropertyEditorTemplate" TargetType="ContentControl">
<props:GridInputPropertyEditor />
</ControlTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Text="Hello Grid" Margin="5" />
<!-- Tell the InputSelectControl what template to load into Property
Window for each Grid Input item -->
<props:InputSelectControl Grid.Row="1"
DataContext="{Binding VmUsedInputs, Mode=OneWay}"
PropertyEditorTemplate="{StaticResource GridPropertyEditorTemplate}" />
</Grid>
</UserControl>
XAML representing my InputSelectControl that displays my list of inputs and a ContentControl place holder for each ListItem where I want my "Popup behavior" for editing properties:
<UserControl x:Class="MyApp.InputSelectControl"
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:props="clr-namespace:PopupButtonDependencyProp"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- Listbox holding our inputs. Assuming whatever we're contained in has
set our DataContext to a valid Input collection-->
<ListBox x:Name="inputsUsed" Grid.Row="1" ItemsSource="{Binding}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
SelectionMode="Multiple" ClipToBounds="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="border" CornerRadius="7">
<StackPanel VerticalAlignment="Stretch" Orientation="Horizontal">
<!-- Input label-->
<TextBlock Text="{Binding Path=Label}" FontWeight ="Bold"
FontSize ="12" FontStyle = "Normal"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5,0,5,0" />
<Expander x:Name="GridPropEditor" Header="Properties"
Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog">
<!-- Properties button - The ContentControl below is rendering
the PropertyEditorTemplate that was set by whoever contains us -->
<ContentControl Template="{Binding PropertyEditorTemplate,
RelativeSource={RelativeSource AncestorType=props:InputSelectControl}}" />
</Expander>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
C# representing my DependencyProperty for injection the property editor template to present on popup.
/// <summary>
/// Interaction logic for InputSelectControl.xaml
/// </summary>
public partial class InputSelectControl : UserControl
{
#region Dependency Property stuff
/// <summary>
/// Dependency Property for control template to be rendered. This
/// lets us adorn the InputSelectControl with content in the Xaml.
/// The content can be different fore each instance of InputSelectControl.
/// </summary>
public static DependencyProperty PropertyEditorTemplateProperty =
DependencyProperty.Register("PropertyEditorTemplate",
typeof(ControlTemplate), typeof(InputSelectControl));
/// <summary>
/// PropertyEditorTemplate. This is how the property is set and get by WPF
/// </summary>
public ControlTemplate PropertyEditorTemplate
{
get { return GetValue(PropertyEditorTemplateProperty) as ControlTemplate; }
set { SetValue(PropertyEditorTemplateProperty, value); }
}
#endregion
/// <summary>
/// Constructor
/// </summary>
public InputSelectControl()
{
InitializeComponent();
}
}
XAML representing my GridInputPropertyEditor which is the template describing the presentation for editing Grid properties. This will be different for a Graph:
<UserControl x:Class="MyApp.GridInputPropertyEditor"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Canvas Panel.ZIndex=”99”>
<!-- Property Editor Control - Assumes DataContext holds to the properties
that need to be edited-->
<StackPanel Orientation="Vertical" Background="WhiteSmoke">
<!-- Lists the properties for a Grid to be edited. We could use
any layout we need here. -->
<ListBox ItemsSource="{Binding Properties}" Background="WhiteSmoke" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="5,5">
<TextBlock Text="{Binding Label}" FontWeight="Bold"/>
<TextBlock Text=":" />
<TextBox Text="{Binding Value}" Margin="10,0" Width="20" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Canvas>
</UserControl>
Using Snoop I was able to figure out that if I set the Z-Index of the ListBox item to a high number my property editor comes to the foreground. If someone sees a better way around this let me know. Otherwise I could use some help coming up with a trigger to raise and lower the zindex based on the item selected.
Okay, after a few trials and tribulations I was able to come up with solution using code behind. I'd be interested in finding a way to do this with triggers, but I'm not sure it's possible with this approach.
Here's the update expander XAML:
<Expander x:Name="GridPropEditor" Header="Properties" Height="Auto" Margin="5,0,0,0"
ToolTip="Open trace property dialog"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}}}"
PreviewMouseDown="GridPropEditor_PreviewMouseDown"
Expanded="GridPropEditor_Expanded">
Here's the code behind I added to my xaml.cs file
//When an expander is expanded, collapse all the other expanders
private void GridPropEditor_Expanded(object sender, RoutedEventArgs e)
{
if (ExpandersSelected == null)
ExpandersSelected = new List<Expander>();
var expander = (sender as Expander);
var listbox = expander.Tag as ListBoxItem;
if (!ExpandersSelected.Contains(expander))
ExpandersSelected.Add(expander);
if (ExpandersSelected != null)
{
foreach(var x in ExpandersSelected)
{
if (x.Equals(expander))
{
listbox.SetValue(Panel.ZIndexProperty, 99);
continue;
}
var l = x.Tag as ListBoxItem;
x.IsExpanded = false;
l.SetValue(Panel.ZIndexProperty, 0);
}
}
}
The code behind solution closes any expanders that might already be opened and brings the currently expanding Expanders container to the foreground by setting the zindex to 99.
Again, if anyone has a better solution I'm all ears.

How to load a grid from XAML codes and add it to a stackpanel dynamically/at runtime?

I generate the XAML codes which actually describe a valid grid control - called the GridXAML. I want to create a grid object and add it to a stackpanel on my form.
How can I 1) create an object from its XAML string value, and 2) add it dynamically to a panel? Please help!
Giving a specific sample context as below.
The generated grid's xaml:
<Grid>
<Textblock Text="abb" />
</Grid>
The current main form of my WPF application.
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="333" Width="111">
<StackPanel x:Name="MyStackPanel" Orientation="Vertical">
<!--I want the grid appear here at runtime-->
</StackPanel>
</Window>
All helps are very much appreciated!
Use the XamlReader's XamlReader.Load() or XamlReader.Parse()

WPF: Focus in a Window and UserControl

I'm trying to get a UserControl to tab properly and am baffled. The logical tree looks like this.
|-Window
-Grid
-TabControl
-TabItem
-StackPanel
-MyUserControl
|-StackPanel
-GroupBox
-Grid
-ComboBox
-Textbox1
-Textbox2
Everything works fine, except when the visibility converter for the ComboBox returns Visibility.Collapsed (don't allow user to change database mode), then when textbox1 is selected, instead of being able to tab through the controls in the UserControl, the focus shifts to a button declared at the bottom of the window. Nothing else apart from the controls displayed has TabIndex or FocusManager properties set.
I'm banging my head against a brick wall and I must be missing something. I've tried IsFocusScope=True/False, played with FocusedElement and nothing works if that ComboBox is invisible (Visibility.Collapsed).
<Window x:Class="MyNamespace.Client.WinInstaller"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FocusManager.FocusedElement="{Binding ElementName=tabWizard}">
<Window.Resources>
<props:Settings x:Key="settings" />
</Window.Resources>
<Grid Grid.IsSharedSizeScope="True">
<!-- row and column definitions omitted -->
<loc:SmallHeader Grid.Row="0" x:Name="headerBranding" HeaderText="Setup" />
<TabControl x:Name="tabWizard" DataContext="{StaticResource settings}" SelectedIndex="0" FocusManager.IsFocusScope="True">
<TabItem x:Name="tbStart" Height="0">
<StackPanel>
<TextBlock Text="Database Mode"/>
<loc:DatabaseSelector x:Name="dbSelector" AllowChangeMode="False" TabIndex="1"
AvailableDatabaseModes="SQLServer" IsPortRequired="False"
DatabaseMode="{Binding Default.DbMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DatabasePath="{Binding Default.DatabasePath,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</TabItem>
...
The top of the user control is below:
<UserControl x:Class="MyNamespace.Client.DatabaseSelector"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root"
FocusManager.IsFocusScope="True"
FocusManager.FocusedElement="{Binding ElementName=cboDbMode}">
<UserControl.Resources>
<conv:DatabaseModeIsFileBased x:Key="DatabaseModeIsFileBased"/>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</UserControl.Resources>
<StackPanel DataContext="{Binding}">
<GroupBox>
<Grid>
<!-- row and column definitions omitted -->
<Label Content="Database Mode"/>
<ComboBox x:Name="cboDbMode" SelectedValue="{Binding ElementName=root,Path=DatabaseMode,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Value" SelectedValuePath="Key" TabIndex="1" Visibility="{Binding AllowChangeMode,ElementName=root,Converter={StaticResource BooleanToVisibilityConverter}}" />
<!-- AllowChangeMode is a DependencyProperty on the UserControl -->
<Grid><!-- row and column definitions omitted -->
<Label "Host"/>
<TextBox x:Name="txtDBHost" Text="{Binding ElementName=root,Path=DatabaseHost,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" TabIndex="2" />
<TextBox x:Name="txtDBPort" Text="{Binding ElementName=root,Path=DatabasePortString,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" TabIndex="3" />
I know this response is quite late... but have you tried:
<UserControl ... KeyboardNavigation.TabNavigation="Local">
Doing so will ensure once your UserControl has recieved focus, you will navigate only through TabStop within your UserControl (instead of worring about conflicting TabIndex values throughout your app). After looping through the TabStops of your UserControl, TabNavigation will resume to the TabStop outside of it.
http://msdn.microsoft.com/en-us/library/system.windows.input.keyboardnavigationmode.aspx
Maybe the problem is that you hide the FocusManager.FocusedElement.
Anyway, you could make life easier by just eliminating some complicating factors:
Remove FocusManager.FocusedElement...
the ComboBox is the first control within anyway.
Remove FocusManager.IsFocusScope...
I suppose it's ok if everytime you enter the usercontrol the first control within will be focussed, not the one that was focussed when you left it before. Just let the usercontrol be "inlined" in the surrounding controls.
Remove the explicit TabIndices in the UserControl. Your layout already implies the same ordering.
If you eliminate these three complicating factors, you might already be done.
Maybe you also have to set your UserControl Focusable=False, s.t. the focus is passed to the first focussable Control within - comboBox or TextBox1.

Resources