How to add controls dynamically to a UserControl through user's XAML? - c#

I want to create a user control that contains a TextBlock and a StackPanel that will allow the user to add his/her own controls to the user control dynamically in XAML.
Here is the sample XAML for my UserControl:
<UserControl x:Class="A1UserControlLibrary.UserControlStackPanel"
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="200" d:DesignWidth="300">
<StackPanel>
<TextBlock Text="I want the user to be able to add any number of controls to the StackPanel below this TextBlock."
FontFamily="Arial" FontSize="12" FontWeight="DemiBold" Margin="5,10,5,10" TextWrapping="Wrap"/>
<StackPanel>
<!-- I want the user to be able to add any number of controls here -->
</StackPanel>
</StackPanel>
</UserControl>
I would like the user to be able to embed this user control in their XAML and add their own controls to the stack panel of the user control:
<uc:A1UserControl_StackPanel x:Name="MyUserControl_Test" Margin="10" Height="100">
<Button Name="MyButton1" Content="Click" Height="30" Width="50"/>
<Button Name="MyButton2" Content="Click" Height="30" Width="50"/>
<Button Name="MyButton3" Content="Click" Height="30" Width="50"/>
</uc:A1UserControl_StackPanel>
Doing this using the above XAML does not work. Any ideas?

You can do that, although not quite like your example. You need two things. The first is to declare a DependencyProperty of type UIElement, of which all controls extend:
public static DependencyProperty InnerContentProperty = DependencyProperty.Register("InnerContent", typeof(UIElement), typeof(YourControl));
public UIElement InnerContent
{
get { return (UIElement)GetValue(InnerContentProperty); }
set { SetValue(InnerContentProperty, value); }
}
The second is to declare a ContentControl in the XAML where you want the content to appear:
<StackPanel>
<TextBlock Text="I want the user to be able to add any number of controls to the StackPanel below this TextBlock."
FontFamily="Arial" FontSize="12" FontWeight="DemiBold" Margin="5,10,5,10" TextWrapping="Wrap"/>
<StackPanel>
<ContentControl Content="{Binding InnerContent, RelativeSource={RelativeSource AncestorType={x:Type YourXmlNamspacePrefix:ContentView}}}" />
</StackPanel>
</StackPanel>
In my opinion, if you use StackPanels, you could find that your content does not get displayed correctly... I'd advise you to use Grids for layout purposes for all but the simplest layout tasks.
Now the one difference to your example is in how you would use your control. The InnerContent property is of type UIElement, which means that it can hold one UIElement. This means that you need to use a container element to display more than one item, but it has the same end result:
<YourXmlNamspacePrefix:YourControl>
<YourXmlNamspacePrefix:YourControl.InnerContent>
<StackPanel x:Name="MyUserControl_Test" Margin="10" Height="100">
<Button Content="Click" Height="30" Width="50"/>
<Button Content="Click" Height="30" Width="50"/>
<Button Content="Click" Height="30" Width="50"/>
</StackPanel>
</YourXmlNamspacePrefix:YourControl.InnerContent>
</YourXmlNamspacePrefix:YourControl>
And the result:
UPDATE >>>
For the record, I know exactly what you want to do. You, it seems, do not understand what I am saying, so I'll try to explain it one last time for you. Add a Button with the Tag property set as I've already shown you:
<Button Tag="MyButton1" Content="Click" Click="ButtonClick" />
Now add a Click handler:
private void ButtonClick(object sender, RoutedEventArgs e)
{
Button button = (Button)sender;
if (button.Tag = "MyButton1") DoSomething();
}
That's all there is to it.

Related

Dock one panel between two others

I have three panels:
The first one is on the top, it holds some buttons only. It will be as high as many buttons will be there.
The second ones should be directly under it, it holds a canvas that should take up most of the window.
The third one is "some kind of" status bar, it will hold some labels and data. It will be also only as high as many labels are added there.
I don't want to hardcode any sizes. So I am docking the first panel to the Top of the parent. The same goes for the 2nd panel (canvas). I am docking it also to the top. The third panel is docked to the bottom.
I can't make the canvas(2nd panel) fill the whole space between 1st panel and 3rd panel. How to do it?
<Window x:Class="MyProject.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:MyProject"
mc:Ignorable="d"
Title="MainWindow" Height="700" Width="800">
<DockPanel LastChildFill="False">
<StackPanel DockPanel.Dock="Top">
<GroupBox x:Name="grBoxSettings" Header="Settings" HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="90" Margin="5,0,5,0">
<WrapPanel x:Name="wrapPanelButtons" HorizontalAlignment="Left" VerticalAlignment="Top" Width="110">
<Button x:Name="btnTest" Content="Test" Margin="5,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Top" Width="100"/>
</WrapPanel>
</GroupBox>
</StackPanel>
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DockPanel.Dock="Top"/>
<DockPanel LastChildFill="False" DockPanel.Dock="Bottom">
<GroupBox Header="Version" DockPanel.Dock="Right">
<Label x:Name="lblVersion"/>
</GroupBox>
</DockPanel>
</DockPanel>
</Window>
Either use a Grid with three RowDefinitions with Height= 1st: Auto 2nd: 1* 3rd: Auto,
Or Simply switch around the order in your DockPanel:
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Top">
<GroupBox x:Name="grBoxSettings" Header="Settings" HorizontalAlignment="Stretch" VerticalAlignment="Top" Height="90" Margin="5,0,5,0">
<WrapPanel x:Name="wrapPanelButtons" HorizontalAlignment="Left" VerticalAlignment="Top" Width="110">
<Button x:Name="btnTest" Content="Test" Margin="5,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Top" Width="100"/>
</WrapPanel>
</GroupBox>
</StackPanel>
<DockPanel LastChildFill="False" DockPanel.Dock="Bottom">
<GroupBox Header="Version" DockPanel.Dock="Right">
<Label x:Name="lblVersion"/>
</GroupBox>
</DockPanel>
<Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</DockPanel>
Also, here's some documentation for the Dock Property:
If you set the LastChildFill property to true, which is the default setting, the last child element of a DockPanel always fills the remaining space, regardless of any other dock value that you set on the last child element. To dock a child in another direction, you must set the LastChildFill property to false and must also set an explicit dock direction on the last child element.

How to open Popup by clicking on grid view item in windows store app?

I want to open popup by clicking on button in gridView element, inside popup I have three options by clicking on that option I want to navigate to another page with the id of the element
i want to open popup on click on button inside the gridView element my code looks like :-
<GridView x:Name="Test" Grid.Row="1" Margin="20,20,20,20" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<Button Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" Height="100" Content="OpenPopup" Click="Button_PointerPressed"></Button>
<Popup x:Name="Mypopup">
<TextBlock Text="hi"/>
</Popup>
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding Title}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextBlockStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
on click event
private void Button_PointerPressed(object sender, RoutedEventArgs e)
{
Mypopup.isopen = true;
}
Error:-The name 'Mypopup' does not exist in the current context
I am a newbie.. so help me
All you have to do is on the Onclick event of your gridview's button do the following code :
popup.isopen = true
Then in your popup if you want to navigate to another page all you have to do is use Navigate on the proper event
Frame.Navigate(typeof(YourPage),YourID);
EDIT : If the buttons in your gridview are added through code and you want to do the event on those then make sure that they have different names such as : GrdButton1/GrdButton2 and then give them an event dynamicly
If they are added in the XAML then simply add it an event there
<Button x:Name="ButtonTest"
Click="ButtonTest_Click"/>
If none of this answers your question please add more details to it

not able to disable a particular text block in custom control

I have a custom control which contains one text-block, one combo-box and one hyper-link button.
<UserControl x:Class="IXExpress.Controls.WorkspaceIndexes"
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:telerikSdk="http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable="d"
Height="Auto" Width="Auto">
<Grid x:Name="LayoutRoot">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center">
<TextBlock x:Name="IndexNameTextBlock" Text="{Binding ApplicationStrings.SelectIndexName, Source={StaticResource ResourceWrapper}, Mode=OneTime}" Margin="3,5" TextAlignment="Left" VerticalAlignment="Center" Visibility="Visible"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<telerikSdk:RadComboBox x:Name="IndexNameCB"
DisplayMemberPath="IndexName"
HorizontalAlignment="Center"
IsDropDownOpen="False"
Margin="3,0,3,5"
MinWidth="150"
Width="150"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Visibility="Visible"
SelectionChanged="IndexNameCB_SelectionChanged"/>
<HyperlinkButton x:Name="CreateNewIndexLink"
Content="Create New"
VerticalContentAlignment="Center"
Click="CreateNewIndexLink_Click"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
I am using it on another page as following:
<StackPanel Orientation="Vertical">
<customControls:WorkspaceIndexes x:Name="WorkspaceIndexes" IsMoreTextRequired="True" Margin="3"/>
</StackPanel>
The issue is, on some condition when I want to disable this control but it only disables combo-box and hyper-link button.
code:
if (my condition)
WorkspaceIndexes.IsEnabled = true;
else
WorkspaceIndexes.IsEnabled = false;
Result:
http://imgur.com/L6tbOwo
I also don't see IsEnabled option for "IndexNameTextBlock" text-block, Why is that?
You can't see the IsEnabled property for the TextBlock because it doesn't have the property. The other Elements are derived from Control, they can be enabled and disabled. The TextBlock is no Control. Disabling a TextBlock would be meaningless. It just displays text. No user interaction possible.
If you need it to be grayed out you have to change either its Foreground color, or reduce its Opacity, or place a semi-transparent Rectangle/Border over it.

Trying to pin image on map with onclick event to page, based on binding.

I build this code so that i have multiple pins on the map. locations and text based on binding:
<Grid x:Name="LayoutRoot" Background="Transparent">
<maps:Map x:Name="myMap" Loaded="myMap_Loaded">
<toolkit:MapExtensions.Children>
<toolkit:MapItemsControl Name="Items">
<toolkit:MapItemsControl.ItemTemplate>
<DataTemplate>
<toolkit:Pushpin GeoCoordinate="{Binding Coordinate}" Tap="Pushpin_Tap_1" >
<toolkit:Pushpin.Template >
<ControlTemplate >
<Canvas>
<Image Source="/App;component/Assets/pin.png"
Width="48" Height="102"
Canvas.Left="-20" Canvas.Top="-102" />
<Border Background="Black" Width="200" Visibility="Visible" x:Name="border1" HorizontalAlignment="Center" >
<StackPanel x:Name="_Stack1" >
<TextBlock x:Name="TextBlock1"
Text="{Binding ID}"
Canvas.Top="-45"
Canvas.Left="5"
Style="{StaticResource PhoneTextNormalStyle }"
Foreground="#FF51FF00" />
<TextBlock
Text="{Binding Name}"
Canvas.Top="-25"
Canvas.Left="5"
Style="{StaticResource PhoneTextContrastStyle }"
Foreground="Red" />
</StackPanel>
</Border>
</Canvas>
</ControlTemplate>
</toolkit:Pushpin.Template>
</toolkit:Pushpin>
</DataTemplate>
</toolkit:MapItemsControl.ItemTemplate>
</toolkit:MapItemsControl>
</toolkit:MapExtensions.Children>
</maps:Map>
</Grid>
Now my question is how i can make a Tab event for the pushpin based on the TextBlock1.Text (I can`t "reach" the textblock1.text in the .cs part)
private void Pushpin_Tap_1(object sender, System.Windows.Input.GestureEventArgs e)
{
NavigationService.Navigate(new Uri("/ExtraPage/Info.xaml?selectedItem=" + TextBlock1.Text(THIS WONT WORK) , UriKind.Relative));
}
What is the best way to do this? Or am I doing it all wrong?
Thanks in advance
You can't access it because it's in the template.
If you want the tap event on TextBlock, just add it there instead of adding it to the pushpin. But I assume that's not really what your need.
If you want the Text value from TextBlock1, what you really want is the ID which is bound to that TextBlock. The sender for that event handler that you have is (probably) a Pushpin. So, what you really want is the DataContext of that Pushpin and the ID property of that DataContext, and from what I can tell, the DataContext is some sort of a location object, so I'll call it Location.
var id = ((sender as Pushpin).DataContext as Location).ID;
And that's what's in your TextBlock - id which you can easily use.
Change 'Location' to whatever your class is.

How do you add items to a Canvas that is part of a UserControl?

I have a usercontrol that contains 2 rows. 1st row has a label and 2nd row has a scrollviewer with a canvas:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="TITLE" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" Name="label1" VerticalAlignment="Top" FontSize="26" />
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<Canvas Background="White" />
</ScrollViewer>
</Grid>
In my main window.xaml I'm trying to add this user control and then add items to it's canvas.
<local:UserCanvas>
<label Content="Test" />
</local:UserCancas>
There problem here is that when I had that label in there it just overrides the "TITLE" label in row 1 of the user control. How do I add things so that they are placed on the cavas of the UserControl?
Instead of defining the Content in your UserControl, define the UserControl.ContentTemplate
For example, instead of writing
<UserControl ...>
<Grid>
...
<Canvas />
...
</Grid>
</UserControl>
use
<UserControl ...>
<UserControl.ContentTemplate>
<DataTemplate>
<Grid>
...
<Canvas>
<ContentPresenter Content="{TemplateBinding Content}"/>
</Canvas>
...
</Grid>
</DataTemplate>
</UserControl.ContentTemplate>
</UserControl>
If you use the first syntax, then specifying the Content when you use your UserControl will result in the existing Content getting overwritten, so your rendered Visual Tree ends up looking like this:
<local:UserCanvas>
<label Content="Test" />
</local:UserCancas>
By using the 2nd syntax, you're wrapping the Content in your ContentTemplate, so the rendered Visual Tree ends up looking like this:
<local:UserCanvas>
<Grid>
...
<Canvas>
<label Content="Test" />
</Canvas>
...
</Grid>
</local:UserCanvas>
I would try adding a public property (that you check and respond to in the user control's Page_Load()) and/or public method (that you just call from outside the control) to the user control which you can then access from your main program. Which way to go depends a bit on how complicated the actions you're needing to take will be. It looks to me like this will be simple enough to handle through the public property + Page_Load() method.

Categories