How to add arbitrary content within a user control - c#

I have a Windows Phone App where I would like to achieve the following:
Define a (user)control which has a Button and an arbitrary control. Once the user clicks the Button, the second control becomes visible and the button is hidden.
I can achieve this directly in the CodeBehind or ViewModel but I would like to have some kind of control that I can reuse on all places where I need this.
I googled a bit and come accross the ContentPresenter.
Control:
<StackPanel Background="Red" x:Name="LayoutRoot">
<Button Content="Dummy"/>
<ContentPresenter/>
</StackPanel>
XAML call:
<controls:MyControl>
<TextBlock Text="Text Content"/>
</controls:MyControl>
But the Contentpresenter cannot be used like this and the Button will never be displayed because the page XAML definition of the content will overwrite the content defined in the user control xaml.
I then found hints to use templates, but I did not yet understand how I could combine this with the logic that I would like to apply here.
Should I create a userControl that has VisibilityProperties for the Button and the other control which are then used by the Template?
Any advice or hint is apprechiated to get an idea where to continue my search.

How about:
Control:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Button Content="ClickMe" Click="Button_Click"/>
<StackPanel Grid.Row="1" x:FieldModifier="public" x:Name="Content"/>
</Grid>
Control.cs:
private void Button_Click(object sender, RoutedEventArgs e)
{
if (Content.Visibility == Visibility.Collapsed)
{
Content.Visibility = Visibility.Visible;
}
else
{
Content.Visibility = Visibility.Collapsed;
}
}
MainPage.xaml:
<local:MyUserControl1 x:Name="MyControl" HorizontalAlignment="Left" Margin="327,124,0,0" VerticalAlignment="Top"/>
MainPage.xaml.cs (adding content):
MyControl.Content.Children.Add(new Button());
MyControl.Content.Children.Add(new Button());
MyControl.Content.Children.Add(new Button());
MyControl.Content.Children.Add(new Button());
MyControl.Content.Children.Add(new Button());
MyControl.Content.Children.Add(new Button());

Related

UWP modal UserControl

I tried to create a modal UserControl because the ContentDialog control is difficult to modify.
How to change background overlay of ContentDialog uwp
The title zone will be on the content zone(like a ContentDialog) but the background should be the same as the owerlay content.
What i try to do
How should i do to create this modal UserControl?
EDIT:
Thanks for your answer!
Actualy i have my control
<UserControl x:Class="TestModal.ModalView">
<Grid Background="Black" Opacity="0.5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="test"/>
</Grid>
</UserControl>
A main page :
Xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Click="Show_Click" />
</Grid>
</Page>
C#:
private void Show_Click(object sender, RoutedEventArgs e)
{
ModalView myModalView = new ModalView();
Content = myModalView;
}
But how can i activate the user control? change content is not what i want to do.
You can easily create a control with an Grid for instance overlaying the complete UI. Give that grid for instance a black color with opacity 0.6 so you can see the underlying UI. Then just fill the Grid with controls you want to show. On events like 'click outside of the data area' (silent dismiss) or clicking a button ('close' or 'save' for instance) you can hide the UI again. And this can be packaged in a UserControl that can be used on other Pages.
If this is an answer to your question, please mark this as answer.

WPF Popup as a separate control written in xaml

Is there chance to create a WPF Popup as a separate control, so it is not inside a window or user control?
I have a popup written in XAML:
<Popup PopupAnimation="Fade" Name="MyPopup" MinWidth="200" MinHeight="100" Width="200" Height="100" Placement="Center" VerticalAlignment="Center" HorizontalAlignment="Center" IsEnabled="True" IsOpen="False">
<Grid Width="Auto" Height="Auto" Background="Gray">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border BorderThickness="2" CornerRadius="8" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.RowSpan="2">
<Border.BorderBrush>
<SolidColorBrush Color="Gray"/>
</Border.BorderBrush>
<Border.Background>
<SolidColorBrush Color="White"/>
</Border.Background>
</Border>
<StackPanel Grid.Row="0">
<Label Foreground="Blue" Content="Popup_Title"/>
</StackPanel>
<GroupBox Grid.Row="1" Header="Popup example content">
<StackPanel>
</StackPanel>
</GroupBox>
</Grid>
</Popup>
Now, on button click of my other control, I would like to do something like this:
private void Button_Click(object sender, RoutedEventArgs e)
{
PopupControl p = new PopupControl();
p.IsOpen = true;
}
By looking at the example of UserControl or Window, I understand that I need to connect this popup with the actual c# class, something like:
public partial class PopupControl : Popup
{
public PopupControl()
{
InitializeComponent();
}
}
and then inside the Popup's XAML add class:
x:Class="WpfApplication1.PopupControl".
but two things:
1) There is no such a thing as x:Class for PopUp
2) Deriving from Popup won't have InitializeComponent(); method.
You almost got it right. While copy-pasting your Popup code in its own XAML file, you didn't add the necessary x namespace that contains the attached property x:Class:
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
As soon as you add this to your root element (Popup), you can add x:Class="WpfApplication1.PopupControl". This will lead to the generation of a partial class in your obj folder that contains the InitializeComponent method that you were missing.
The easiest way to do that is by
creating a new UserControl from the "Add Item" dialog.
Then, rename the root element from UserControl to Popup.
Finally, from the code behind class, just remove : UserControl.

ScrollViewer is visible?

I'm using ComputedHorizontalScrollBarVisibility but this doesn't work when you set the HorizontalScrollBarVisibility to "Hidden".
What I'm trying to achieve is knowing if a ScrollViewer should be visible but without showing the ScrollViewer. Then bind that result to show the buttons that control the ScrollViewer (in this case the `StackPanel below).
XAML
<ScrollViewer HorizontalScrollBarVisibility="Auto" x:Name="Scroll">
.....
</ScrollViewer>
<StackPanel Visibility="{Binding ElementName=Scroll, Path=ComputedHorizontalScrollBarVisibility}">
<Button Grid.Column="0" Content="Left" HorizontalAlignment="Left" Click="..."/>
<Button Grid.Column="1" Content="Right" HorizontalAlignment="Right" Click="..."/>
</StackPanel >
If you need to control the way the ScrollViewer (or any control, really) is laid out, consider using a ControlTemplate, which is accesible in any Control's Template property. as this will allow you to bind to the object itself and values passed in to it, and provide such templating. This may, however, involve needing to deal with the computations to show the exact part of your control which is visible.
You can get what you want by simple adding up the width of content elements inside ScrollViewer, e.g. if you have a StackPanel (with Orientation=Horizontal) inside ScrollViewer then add up the width of each child element in the StackPanel and compare it with ActualWidth of ScrollViewer. if the sum is less greater than the ActualWidth of ScrollViewer then you need to scroll it.
For more details refer this link
In my experience, the scroll viewer property values can be stale until the next layout pass. It's code-behind in my simple example below but this does work the way you want.
I create a dependency property called "ShowScrollButtons". You can probably watch for extent and viewport size changes and automatically recompute the property.
When the scroll content size changes, I trigger a re-evaluate of ShowScrollButtons. Note the call to UpdateLayout to make sure the extent and viewport sizes are up-to-date. Again, it's a sample so I'm only checking the Width here for left/right scroll buttons
private void UpdateScrollButtonVis()
{
UpdateLayout();
ShowScrollButtons = (Scroll.ExtentHeight > Scroll.ViewportWidth);
}
In XAML...
<Window.Resources>
<BooleanToVisibilityConverter x:Key="boolvis"/>
</Window.Resources>
<Grid x:Name="theGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Width="100" Height="100" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" x:Name="Scroll">
<Canvas x:Name="theCanvas" Width="300" Height="300" Background="Green"/>
</ScrollViewer>
<StackPanel Grid.Row="1" Visibility="{Binding ShowScrollButtons,Converter={StaticResource boolvis}}">
<Button Grid.Column="0" Content="Left" HorizontalAlignment="Left" />
<Button Grid.Column="1" Content="Right" HorizontalAlignment="Right" />
</StackPanel >
<Button x:Name="toggle" Grid.Row="2" Height="25" Width="100" Click="toggle_Click">Toggle</Button>
</Grid>
Update:
How about a new approach works with multiple scroll viewers and StackPanels without code-behind.
Use an Attached Property to control the external button visibility:
public class ScrollViewWatcher
{
public static readonly DependencyProperty HorizontalButtonVisibility = DependencyProperty.RegisterAttached(
"HorizontalButtonVisibility",
typeof(Visibility),
typeof(ScrollViewWatcher),
new FrameworkPropertyMetadata(Visibility.Visible,
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsMeasure)
);
public static Visibility GetHorizontalButtonVisiblity(UIElement element)
{
return (Visibility)element.GetValue(HorizontalButtonVisibility);
}
public static void SetHorizontalButtonVisibility(UIElement element, Visibility value)
{
element.SetValue(HorizontalButtonVisibility, value);
ScrollViewer sv = element as ScrollViewer;
if (sv != null)
{
sv.ScrollChanged -= sv_ScrollChanged;
sv.ScrollChanged += sv_ScrollChanged;
}
}
static void sv_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
var sv = sender as ScrollViewer;
if (sv != null)
{
var vis = sv.ExtentHeight > sv.ViewportWidth ? Visibility.Visible : Visibility.Hidden;
sv.SetValue(HorizontalButtonVisibility, vis);
}
}
}
Then in XAML, bind to the appropriate ScrollViewer like this:
<ScrollViewer
x:Name="sv1" local:ScrollViewWatcher.HorizontalButtonVisibility="Visible"
Grid.Row="0" Width="100" Height="100" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" >
<Canvas x:Name="theCanvas" Width="300" Height="300" Background="Green"/>
</ScrollViewer>
<StackPanel Grid.Row="1" Visibility="{Binding ElementName=sv1,Path=(local:ScrollViewWatcher.HorizontalButtonVisibility), Mode=OneWay}">
<Button Grid.Column="0" Content="Left" HorizontalAlignment="Left" />
<Button Grid.Column="1" Content="Right" HorizontalAlignment="Right" />
</StackPanel >
This works great in my test. This was a fun challenge. Maybe someone can enlighten us with a better approach but I'm pretty happy with this.
Thank you all for the answers but finally got a workaround a bit easier, instead of binding the visibility from the StackPanel of the buttons just call a ScrollChanged in the ScrollViewer and then in code check for the ComputedHorizontalScrollBarVisibility and change visibilities depending on the result.
XAML
<ScrollViewer HorizontalScrollBarVisibility="Auto" x:Name="Scroll" ScrollChanged="Scroll_ScrollChanged">
.....
</ScrollViewer>
<StackPanel x:Name="BPanel" Visibility="Hidden">
<Button Grid.Column="0" Content="Left" HorizontalAlignment="Left" Click="..."/>
<Button Grid.Column="1" Content="Right" HorizontalAlignment="Right" Click="..."/>
</StackPanel >
C#
private void Scroll_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ScrollViewer scroll = (ScrollViewer)sender;
if(scroll.HorizontalScrollBarVisibility == ScrollBarVisibility.Auto)
{
if (scroll.ComputedHorizontalScrollBarVisibility == Visibility.Visible)
{
scroll.HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
BPanel.Visibility = Visibility.Visible;
}
}
}

wpf user control - how to set text from outer uc

i have a problem , i have 2 uc that displayed in main windows.
i want when i press on button in the first uc the text in the other uc will change
this the axml i use and the code
first uc
<Grid.RowDefinitions>
<RowDefinition ></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Width="40" Height="40" Name="playbtn" HorizontalAlignment="Left" Grid.Row="0" Grid.Column="0" Click="playbtn_Click" >
<Button.Content>
<Image Source="/img/player_play.png" ></Image>
</Button.Content>
</Button>
<Button Width="40" Height="40" Name="pausebtn" Grid.Column="1" Grid.Row="0" >
<Button.Content>
<Image Source="/img/player_pause.png" ></Image>
</Button.Content>
</Button>
<Button Width="40" Height="40" Name="stopbtn" Grid.Column="2" Grid.Row="0" >
<Button.Content>
<Image Source="/img/player_stop.png" ></Image>
</Button.Content>
</Button>
</Grid>
</StackPanel>
</Grid>
</StackPanel>
secound uc <TextBlock Name="Progresstimertext" Text="00:00:00" FontSize="20"></TextBlock>
so i want to press on start button and the timer will be change? how to
10x
This should be managed in the View Model of your Main Window, as this seems the only location that has knowledge of both controls and it sounds as though each of your user control's shouldn't necessarily have knowledge of each other.
Ideally, you should have a reference to each of your user controls in the code-behind (or view model class is you have a dedicated one).
If in code behind, you would simply expose these by giving them names.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="your controls reference here">
<Grid>
<Grid.ColumnDefinitions>
</ColumnDefinition>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:UserControl1 x:Name="Control1"/>
<controls:UserControl2 x:Name="Control2" Grid.Column="1"/>
</Grid>
</Window>
Now you can reference them in your code behind by name Control1 and Control2. These user control classes should be exposing the properties that you want to control, in your case ProgressTimerText, such that, in the code-behind, you can set it easily such as
Control2.ProgressTimerText = "00:00:00";
Where to do this? Well you probably want to create a Stopped event on your Control1, that you can attach to your code behind - in your case, something like Stopped. In your UserControl1 class you should declare something like
public event EventHandler Stopped;
And then in the local event handler for the Click of the stop button, invoke that event.
private void Stop_Click(object sender, RoutedEventArgs e)
{
if (Stopped != null)
Stopped(this, e);
}
Attach that in your MainWindow:
<controls:UserControl1 x:Name="Control1" Stopped=Control1_Stopped/>
Now in your code-behind you should have something like:
private void Control1_Stopped(object sender, RoutedEventArgs e)
{
Control2.ProgressTimerText = "00:00:00";
}

Custom ListView Control

So, I have been looking for solution more than 12 hours(but without success). How should I change ListView ControlTemplate to get effect like this:
(This question is about this buttons that working like scrollview)
Have you another ideas how to create control like this?
It's vertical representation, but idea is understood: hide scrollbars and manipulate them manually. For more responsive UI you'll need to subscribe to MouseDown event instead of Click, also NullReference exceptions are possible on every line of Grid_Click().
XAML:
<ListView.Template>
<ControlTemplate>
<Grid ButtonBase.Click="Grid_Click">
<Grid.RowDefinitions>
<RowDefinition Height="16"/>
<RowDefinition Height="*"/>
<RowDefinition Height="16"/>
</Grid.RowDefinitions>
<Button Content="^" Grid.Row="0"/>
<Button Content="v" Grid.Row="2"/>
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Hidden">
<ItemsPresenter/>
</ScrollViewer>
</Grid>
</ControlTemplate>
</ListView.Template>
Code:
private void Grid_Click(object sender, RoutedEventArgs e) {
bool down = (e.OriginalSource as Button).Content as string == "v";
var scroller = VisualTreeHelper.GetChild((e.OriginalSource as Button).Parent, 2) as ScrollViewer;
scroller.ScrollToVerticalOffset(scroller.VerticalOffset + (down ? 1 : -1));
}
Magical number 2 in GetChild() is index of ScrollViewer inside its parent (Grid).

Categories

Resources