Access XAML Control In DataTemplate From CodeBehind? - c#

I have a control that I am unable to access in the codebehind, and I believe it is because it is defined in a DataTempalte.
The overall control is a slide show carousel. Each slide can be an Image or a MediaElement (video), the contents of which are defined in an ItemSource binding. The carousel is on a timer to switch from one slide to the next. Each time the slide changes I fire an event to that effect.
When I hit a slide with a video I'd like to stop the slide timer (done that) and start the video, which is where I've run into a problem. I can not access the MediaPlayer element Name from my codebehind. My assumption at this point is because it is a DataTemplate.
Is this assumption correct? If so, how can I get access to this control from the codebehind, or (more to the point) have it start playing when the slide comes up?
<ctrl:AutoScrollCarousel ...>
<ctrl:AutoScrollCarousel.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ctrl:AutoScrollCarousel.ItemsPanel>
<ctrl:AutoScrollCarousel.ItemTemplate>
<DataTemplate>
<Border x:Name="Border" VerticalAlignment="Center"
Width="{Binding ActualWidth, RelativeSource={RelativeSource AncestorType={x:Type UserControl},Mode=FindAncestor}}">
<Grid Background="White">
...
<Image Source="{Binding ContentImage}" Grid.Row="1" Grid.Column="1" Stretch="UniformToFill"
HorizontalAlignment="Center"
Visibility="{Binding ContentImage, Converter={StaticResource VisibilityConverter}}" />
<MediaElement Name="MediaPlayer" Source="{Binding ContentVideo}" Grid.Row="1" Grid.Column="1" Stretch="UniformToFill" LoadedBehavior="Play"
Visibility="{Binding ContentVideo, Converter={StaticResource VisibilityConverter}}" MediaEnded="MediaPlayer_MediaEnded" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Title}" Foreground="Black"
FontFamily="Segoe UI" FontWeight="Light" HorizontalAlignment="Left" FontSize="75" Margin="0" VerticalAlignment="Center" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ContentHeadline}" Foreground="Black"
FontFamily="Segoe UI" FontWeight="Light" HorizontalAlignment="Left" FontSize="50" VerticalAlignment="Center"
TextWrapping="Wrap">
</TextBlock>
</Grid>
</Border>
</DataTemplate>
</ctrl:AutoScrollCarousel.ItemTemplate>
</ctrl:AutoScrollCarousel>

WPF provides a simple and straightforward way to access named elements that are generated from DataTemplates. It is explained in the MSDN article How to: Find DataTemplate-Generated Elements.
Assumed that your AutoScrollCarousel is derived from ItemsControl, you would get the ContentPresenter that is the container of an item like this:
AutoScrollCarousel carousel = ...
object item = ...
var contentPresenter =
carousel.ItemContainerGenerator.ContainerFromItem(item) as ContentPresenter;
From the ContentPresenter you would get the named element in the DataTemplate by means of the FindName method:
var dataTemplate = contentPresenter.ContentTemplate;
var mediaPlayer = dataTemplate.FindName("MediaPlayer", contentPresenter) as MediaElement;

I would normally recommend not to touch UIElements from code... but the MediaElement is a special case... maybe you should wrap the whole template inside a usercontrol (maybe with some custom DepProps) and that will give you better control over the whole thing.
Edit: Another approach would be to create a Behavior with a couple of properties (such as IsPlaying) and manipulate the mediaelement from there. Then you could use this behavior in the XAML of the DataTemplate, with no need for code behind or usercontrols.

Related

Can't binding in ToolTip

I'm working with ToolTip in WPF C#. I want to bind an element property to ToolTip content, but the output is empty. Initially, I used the binding below, and it did not work.
Content="{Binding ElementName=txtf1, Path=Text}"
So I found related solutions here, but the result did not change. Can anyone point me where I'm going wrong?
<StackPanel Height="Auto" Margin="15,10,15,0" Width="74">
<Image Source="/Image/AutoCAD.png" Width="50"/>
<TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
<StackPanel.ToolTip>
<ToolTip DataContext="{Binding Path=PlacementTarget.DataContext ,
RelativeSource={RelativeSource Self}}">
<Label Content="{Binding ElementName=txtf1, Path=Text}"/>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
[...] initially, I use Content="{Binding ElementName=txtf1, Path=Text}" and it not work.
Yes, that is because each window in WPF has its own visual tree with elements and a ToolTip and other popups are actually displayed in a separate window. ElementName bindings do not work here (RelativeSource too), since these are different XAML namescopes.
The question you cited is the right approach, but you applied it the wrong way. In theory you can use the DataContext property as binding indirection for the ToolTip, but you should not.
<StackPanel DataContext="{Binding Text, ElementName=txtf1}" Height="Auto" Margin="15,10,15,0" Width="74">
<Image Source="/Image/AutoCAD.png" Width="50"/>
<TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
<StackPanel.ToolTip>
<ToolTip DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<Label Content="{Binding}"/>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
The above code works, but the DataContext property serves a core purpose in WPF for data binding. Do not abuse this property for this purpose or you break data-binding down the visual tree. Apart from that, the binding on Label and ToolTip is redundant, as you can see below. There is another property called Tag that you can assign any value to.
Gets or sets an arbitrary object value that can be used to store custom information about this element.
<StackPanel Tag="{Binding Text, ElementName=txtf1}" Height="Auto" Margin="15,10,15,0" Width="74">
<Image Source="/Image/AutoCAD.png" Width="50"/>
<TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
<StackPanel.ToolTip>
<ToolTip>
<Label Content="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
You can use a RelativeSource binding to refer to the ToolTip.Tag directly from the label. If there are multiple bindings like this, you could consider creating custom attached properties for each of them and bind them the same way.
I reference here, How to binding other element in ToolTip
You have not carefully read the topic you are referring to.
ToolTip, ContextMenu, Popup (and some other elements) pop up over the contents of the Window without changing it.
Think for yourself: How can you introduce new elements into a Window without changing the visual tree of this Window?
Answer: this cannot be done in any way.
Therefore, these pop-up elements are implemented as the contents of a new small Window shown above the main Window (the one in which they were called).
And since these are DIFFERENT Windows, they have different visual trees.
Therefore, it is impossible to find elements from the main Window in the tree of the Pop-up Window (Bindings of the ElementName and FindAncestor types).
You can use an element reference (Binding Sourse = {x: Reference ...}), since references are resolved at compile time and without regard to the visual tree.
Example:
<StackPanel Height="Auto" Margin="15,10,15,0" Width="74">
<Image Source="/Image/AutoCAD.png" Width="50"/>
<TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
<StackPanel.ToolTip>
<ToolTip>
<Label Content="{Binding Source={x:Reference txtf1}, Path=Text}"/>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
In addition, the Popup's DataContext inherits from the element in which It is created.
In your case, this is from StackPanel.
Therefore, you can simply set the default Bindings to receive data.
Example:
<StackPanel Height="Auto" Margin="15,10,15,0" Width="74">
<Image Source="/Image/AutoCAD.png" Width="50"/>
<TextBlock x:Name="txtf1" Text="{Binding SomeViewModelProperty}"/>
<StackPanel.ToolTip>
<ToolTip>
<Label Content="{Binding SomeViewModelProperty}"/>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
The PlacementTarget property is used to change the target element for the popup.
DataContext inherits from this element.
If it is not specified, the DataContext is inherited from the element in which the flyout is specified.
You don't specify it, so it is null, which is what your binding returns.
Example:
<StackPanel DataContext="{Binding Text, ElementName=txtf1}" Height="Auto" Margin="15,10,15,0" Width="74">
<Image Source="/Image/AutoCAD.png" Width="50"/>
<TextBlock x:Name="txtf1" Text="{Binding SomeViewModelProperty}"/>
<StackPanel.ToolTip>
<ToolTip PlacementTarget="{Binding ElementName=txtf1}"
DataContext="{Binding Path=PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
<Label Content="{Binding SomeViewModelProperty}"/>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>

Trying to apply SolidColorBrush resource to HeaderedContentControl BorderBrush

I'd like to link a SolidColorBrush from my Window to Another SolidColorBrush in My dictionary. I didn't find something like this , and may be it's not possible ...
here is the code in my "ResourceDictionary.xaml"
<SolidColorBrush x:Key="BrushBlueTransparent" Color="#33006D8F"/>
And in my windows i want a link to this resource like this :
<SolidColorBrush x:Key="ControlColor" Color="{Binding Source={DynamicResource BrushEvasanOrange}}"/>
For now, this code don't work ...
I want to use this link because i want to use this resource in my page in a multiple "" and if the color had to be change in the futur it could be easy to change with this way.
The Brush resource is used like this:
<HeaderedContentControl
x:Name="_demandeur"
BorderBrush="{DynamicResource BrushEncadre}"
BorderThickness="1"
Padding="10"
Margin="0,20,0,0"
Header="{x:Static p:Resources.EV_Demandeur}"
>
<WrapPanel
Margin="0"
Orientation="Horizontal"
HorizontalAlignment="Left"
>
<TextBlock
TextWrapping="Wrap"
FontWeight="Normal"
Text="text"
/>
</WrapPanel>
</HeaderedContentControl>
It sounds like your problem is that HeaderedContentControl ignores its BorderBrush property. There are two ways to fix this: One is to replace the HeaderedContentControl's Template with one that displays a border around the content, but that's a lot of trouble. Another is to use a subclass of HeaderedContentControl which already has a template that does you want (we'll get to that last).
One very simple option is to simply put a Border around the control, and move the Margin to the Border as well, so the orange border line will be inside the margin. This isn't the right answer in your specific case, but it's a good general answer to "how do I put a border around things in XAML?"
<Border
BorderBrush="{StaticResource BrushEncadre}"
BorderThickness="1"
Margin="0,20,0,0"
>
<HeaderedContentControl
x:Name="_demandeur"
Padding="10"
Header="{x:Static p:Resources.EV_Demandeur}"
>
<WrapPanel
Margin="0"
Orientation="Horizontal"
HorizontalAlignment="Left" >
<TextBlock
TextWrapping="Wrap"
FontWeight="Normal"
Text="text"
/>
</WrapPanel>
</HeaderedContentControl>
</Border>
But I'm wondering if HeaderedContentControl is really what you want here. HeaderedContentControl is a base class for a variety of controls which display content with a header. The subclasses are much more commonly used, and I have a feeling that what you really want here is GroupBox, which is one of those subclasses. You'd use it just the way you were using HeaderedContentControl:
<GroupBox
x:Name="_demandeur"
Padding="10"
Margin="0,20,0,0"
Header="{x:Static p:Resources.EV_Demandeur}"
BorderBrush="{StaticResource BrushEncadre}"
BorderThickness="1"
>
<WrapPanel
Margin="0"
Orientation="Horizontal"
HorizontalAlignment="Left" >
<TextBlock
TextWrapping="Wrap"
FontWeight="Normal"
Text="text"
/>
</WrapPanel>
</GroupBox>

Inline object position in XAML WP8

I want to make checkbox and textbloxk parallel or inline in my XAML WP8, but it seems the checkbox is upside and the textblock is below the checkbox. Any suggest how?
<Grid Margin="0,522,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<StackPanel HorizontalAlignment="Center">
<CheckBox x:Name="Accept"/>
<TextBlock Text="I Accept Terms and Conditions" VerticalAlignment="Top"/>
</StackPanel>
</Grid>
You don't need TextBlock for this. Set CheckBox.Content:
<CheckBox x:Name="Accept" Content="I Accept Terms and Conditions"/>
and for other cases if want to stack elements horizontally then set Orientation="Horizontal" on you StackPanel
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">

set visibility of image dynamically in expanderview

I am trying to implement something where I need to display list of people and a green icon if they are online. these people are grouped by some categories. I am using an expanderview toolkit control to display the list. So how do I set the icon image to be visible dynamically ? I have tried something like this which didnt work.
<DataTemplate x:Key="groupsItemTemplate">
<StackPanel Orientation="Horizontal" Margin="30,5,0,0"">
<Image Height="30" Width="30" Source="/Assets/Online.png" Margin="10,5,0,0" Visibility="{Binding IsFriendOnline}"></Image>
<TextBlock TextWrapping="NoWrap" FontFamily="Segoe WP Light" FontSize="24" Margin="8,0,0,0" VerticalAlignment="Center" HorizontalAlignment="left" Height="auto" Width="300" Text="{Binding FriendName}"></TextBlock>
</StackPanel>
</DataTemplate>
IsFriendOnline is an integer property.
Firstly, you need to use a converter in order to convert the value of your IsFriendOnline property to the Visibility enum that you require.
WPF has a "BooleanToVisibilityConverter" built in so if you have the ability to change the IsFriendOnline to a boolean value (which sounds like it makes a little more sense anyway) I would go down this route... if its imperative that the property is an integer then you will need to roll your own converter which isnt difficult.
The syntax would look something like this when you have a converter (my code below assumes IsFriendOnline is a boolean)...
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<DataTemplate x:Key="groupsItemTemplate">
<StackPanel Orientation="Horizontal" Margin="30,5,0,0"">
<Image Height="30" Width="30" Source="/Assets/Online.png" Margin="10,5,0,0" Visibility="{Binding IsFriendOnline, Converter={StaticResource BooleanToVisibilityConverter}}"></Image>
<TextBlock TextWrapping="NoWrap" FontFamily="Segoe WP Light" FontSize="24" Margin="8,0,0,0" VerticalAlignment="Center" HorizontalAlignment="left" Height="auto" Width="300" Text="{Binding FriendName}"></TextBlock>
</StackPanel>
</DataTemplate>
Hope this helps...

Create control instance in WPF

I want to load multiple images inside a wrappanel, for each image I show a thumbnail and some image details with this code
<Border BorderThickness="1" BorderBrush="#FFD0D1D7" Padding="5" Margin="10,10,0,0">
<StackPanel Orientation="Horizontal">
<!--image and dimensions-->
<Grid Width="88" Height="55">
<Image Source="C:\img1.jpg" Width="88" Height="55"/>
<TextBlock Background="#B2000000" Foreground="White" Height="16" TextAlignment="Center" VerticalAlignment="Bottom">1280x1024</TextBlock>
</Grid>
<!--name, type and size-->
<StackPanel Orientation="Vertical" Margin="5,0,0,0" VerticalAlignment="Center">
<TextBlock Margin="1" Foreground="#FF787878">img13.jpg</TextBlock>
<TextBlock Margin="1" Foreground="#FF787878">Type: JPEG</TextBlock>
<TextBlock Margin="1" Foreground="#FF787878">Size: 321 KB</TextBlock>
</StackPanel>
</StackPanel>
</Border>
But the images are loaded at runtime, and I need some way to create instances of the above code to show the image, dimensions, name, type and size
I tried this solution https://stackoverflow.com/a/4991028/962284
StringBuilder sb = new StringBuilder();
// use xaml to declare a button as string containing xaml
sb.Append(#"<Border BorderThickness='1' BorderBrush='#FFD0D1D7' Padding='5' Margin='10,10,0,0'>
<StackPanel Orientation='Horizontal'>
<!--image and dimensions-->
<Grid Width='88' Height='55'>
<Image Source='C:\img1.jpg' Width='88' Height='55'/>
<TextBlock Background='#B2000000' Foreground='White' Height='16' TextAlignment='Center' VerticalAlignment='Bottom'>1280x1024</TextBlock>
</Grid>
<!--name, type and size-->
<StackPanel Orientation='Vertical' Margin='5,0,0,0' VerticalAlignment='Center'>
<TextBlock Margin='1' Foreground='#FF787878'>img13.jpg</TextBlock>
<TextBlock Margin='1' Foreground='#FF787878'>Type: JPEG</TextBlock>
<TextBlock Margin='1' Foreground='#FF787878'>Size: 321 KB</TextBlock>
</StackPanel>
</StackPanel>
</Border>");
FrameworkElement thumb = (FrameworkElement)System.Windows.Markup.XamlReader.Parse(sb.ToString());
ThumbnailContainer.Children.Add(thumb);
but I get the following error
I also tried with styles, but styles doesnt support multiple parameters (to specify the textblocks: dimensions, size, name, type and size) just "TemplateBinding Tag" for 1 value
What can I do to create instances of the first code to show multiple images at runtime?
Wow. That so looks like the hard way to do things. Time to embrace WPF and XAML.
I had a post about this exact same thing that wasn't quite finished. I took time to finish it for you. I even used your XAML snippet (well, a modified version of it) in the example, just for you.
http://www.wpfsharp.com/2012/10/23/displaying-images-from-a-folder-with-details-in-wpf/
Clemens is correct in his comment to use an ItemsControl.
Yes your approach is wrong and you should be doing this some other way but to get your code snippet to work try adding xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" to your Border element in the string you are building. I suspect that is the error.

Categories

Resources