Access DataTemplate controls in code behind - c#

I have problem with this code:
<ListBox x:Name="lbInvoice" ItemsSource="{Binding ocItemsinInvoice}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<ToggleButton x:Name="btnInvoiceItem">
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="btnInvoiceQuantity" Content="{Binding Quantity}"/>
<TextBlock Text="{Binding Item.ItemName}" Width="175" Padding="7,5,0,0"/>
</StackPanel>
</ToggleButton>
<Popup x:Name="popQuantity" Closed="popQuantity_Closed" PlacementTarget="{Binding ElementName=btnInvoiceQuantity}" IsOpen="{Binding IsChecked,ElementName=btnInvoiceQuantity}">
<Grid>
<TextBlock x:Name="tbUnitPrice" Text="Unit Price"/>
<Button x:Name="btnClosePopup" Click="btnClosePopup_Click">
</Grid>
</Popup>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
In code behind in btnClosePopup click event I can't access to popup to close it and do some other changes on it.
I have tried to use FindName() method but it doesn't work for me
var template = lbInvoice.Template;
var myControl = (Popup)template.FindName("popQuantity", lbInvoice);
Please can you help and tell me how do I access to controls that inside DataTemplate in code behind?

You don't have to do it in code behind and if you change Popup.IsOpen in code it won't appear again as you'll lose you binding. You need to set IsChecked on ToggleButton to false and you can do it with EventTrigger
<Button Content="Close" x:Name="btnClosePopup">
<Button.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetName=" btnInvoiceQuantity" Storyboard.TargetProperty="IsChecked">
<DiscreteBooleanKeyFrame Value="False" KeyTime="0:0:0"/>
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Button.Triggers>
</Button>

You have already to Open/Close this Popup in this line:
IsOpen="{Binding IsChecked, ElementName=btnInvoiceQuantity}"
As an alternative answer from #dkozl, you can close the Popup in such a way:
<Popup x:Name="popQuantity"
IsOpen="{Binding Path=IsChecked, ElementName=btnInvoiceQuantity}">
<Grid Width="200" Height="200" Background="Gainsboro">
<TextBlock Text="Unit Price" />
<ToggleButton x:Name="btnClosePopup"
IsChecked="{Binding Path=IsChecked, ElementName=btnInvoiceQuantity}"
Content="Close"
Width="100"
Height="30" />
</Grid>
</Popup>
Or you can directly specify a property IsOpen of Popup:
<ToggleButton x:Name="btnClosePopup"
IsChecked="{Binding Path=IsOpen, ElementName=popQuantity}" ... />
But in this case at the background color of Button will be in state of IsChecked="True". To avoid this, without creating a new Template for your Control, you can use a system style of flat button:
<ToggleButton x:Name="btnClosePopup"
Style="{StaticResource {x:Static ToolBar.ToggleButtonStyleKey}}" ... />

Related

Binding Button Click Event to a Method in ViewModel with Interaction.Triggers when Buttons are dynamically created at runtime

In my C# WPF MVVM pattern application, I have an ItemsControl in my View that draws Lines and Buttons on a Canvas based on a bound ItemsSource, defined in XAML as below:
<Window.DataContext>
<viewmodels:MainWindowViewModel />
</Window.DataContext>
.
.
.
<ItemsControl
x:Name="DiagramViewCanvas"
ItemsSource="{Binding DiagramObjects, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:LineObject}">
<Line
X1="{Binding XStart}"
Y1="{Binding YStart}"
X2="{Binding XEnd}"
Y2="{Binding YEnd}"
Stroke="White"
StrokeThickness="1"
SnapsToDevicePixels="True"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ButtonObject}">
<Button
Style="{DynamicResource MyDiagramButtonStyle}"
Width="225"
Height="30"
Content="{Binding Content}"
FontSize="13"
SnapsToDevicePixels="True">
</Button>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Black" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding XPosition, UpdateSourceTrigger=PropertyChanged}" />
<Setter Property="Canvas.Top" Value="{Binding YPosition, UpdateSourceTrigger=PropertyChanged}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
This code works completely fine. My question is how to bind the Buttons' Click event to a method in the ViewModel (MainWindowViewModel).
Option 1 (which I don't want to use due MVVM pattern): If I try a simple Click event definition as below ...
<Button
Style="{DynamicResource MyDiagramButtonStyle}"
Width="225"
Height="30"
Content="{Binding Content}"
FontSize="13"
SnapsToDevicePixels="True"
Click="OnButtonClick"/>
... where OnButtonClick is defined in my XAML codebehind, the OnButtonClick method is successfully called and executed for each Button that is created at runtime. It works fine.
Option 2: However, if I try to use Interaction.Triggers as below (which is the approach I regularly use without any problems in my code) to avoid placing code in code behind ...
<Button
Style="{DynamicResource MyDiagramButtonStyle}"
Width="225"
Height="30"
Content="{Binding Content}"
FontSize="13"
SnapsToDevicePixels="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction TargetObject="{Binding}" MethodName="OnButtonClick"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
... where OnButtonClick is defined in my MainWindowViewModel ...
public void OnButtonClick(object sender, RoutedEventArgs e)
{
if (sender is Button btn)
{
// do something
}
}
... I get the following error:
System.ArgumentException: 'Could not find method named 'OnButtonClick' on object of type 'ButtonObject' that matches the expected signature.'
Question 1: Am I making a basic mistake in my implementation of interaction triggers (I have many other interaction triggers in my code that work completely fine)? Or is it that Interaction.Triggers do not work in this scenario where the Buttons are created dynamically at runtime?
Question 2: Should I be using ICommand instead (for example as mentioned in Binding Commands to Events?)?
Thanks for any direction on what I am doing wrong.
Found a solution using Interaction.Triggers:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:CallMethodAction TargetObject="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}, Path=DataContext}" MethodName="OnButtonClick"/>
</i:EventTrigger>
</i:Interaction.Triggers>

Extended Textbox is behind other elements in WPF

It's the first time i am learning WPF with C#.
So i have a textbox which is extended when i click on a button. The problem is when i extend it,it remains behind my other elements(textboxes etc...)
Here is the xml code for the textbox.
<StackPanel Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="3" Grid.RowSpan="5" Panel.ZIndex="0" Visibility="Collapsed" Name="descriptionPanel" Margin="2,0,0,0" Background="White">
<Border BorderBrush="DarkGray" BorderThickness="1" CornerRadius="5">
<StackPanel Orientation="Horizontal">
<TextBox Name="descriptionTextBoxExtended"
Margin="10,10,10,10"
Style="{StaticResource expandedTextBox}"
Text="{Binding Path=Description, Mode=TwoWay, ValidatesOnExceptions=true, NotifyOnValidationError=true, TargetNullValue=''}"
IsReadOnly="{Binding RelativeSource={RelativeSource AncestorLevel=1,AncestorType=Control}, Path=SecurityLevel2ReadOnly, Mode=OneWay}"
/>
<Button Name="descriptionHide" Style="{StaticResource MinusButton}" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,10,10" Click="descriptionHide_Click" />
</StackPanel>
</Border>
</StackPanel>
And here is the C#
private void exDescription_Click(object sender, RoutedEventArgs e)
{
descriptionPanel.Visibility = Visibility.Visible;
}
This is how it looks when i run it before i click the extend button:
And this is how it looks when i extend the textbox:
I want all the highlighted components to be behind the textbox.
You should give us the code of the other elements beside "descriptionPanel", but as mm8 pointed out, it seems that your elements are not in the same Panel.

Content Control and Button Flyout

I am making a UWP app and I have come across a problem. I want to make a StackPanel which hosts two ComboBoxes and one TextBox. I can show it in the app if I create it inside the Grid and it works as expected. But for smaller screen devices I want to show a Button in place of the StackPanel and move the StackPanel to the button flyout.
I have tried to add the StackPanel to a ContentControl and then set it as the Flyout but it doesn't work. Flyout needs a FlyoutPresenter control to be able to show the flyout.
I don't want to create multiple StackPanel controls because of the naming collisions, but I do want it simple so I need to make changes to one side of the controls and when the user switches the screen or the view the smaller screen also shows the same stuff.
Can someone help me here? Maybe just point me in the right direction so I can figure it out on my own. Any help will be appreciated. Thanks
StackPanel control:
<StackPanel Orientation="Vertical"
x:Name="PageOptionsPanel"
HorizontalAlignment="Right">
<AppBarButton Label="Refresh"
Icon="Refresh"
Tapped="PageOptions_Tapped"/>
<RelativePanel Margin="10,0">
<TextBlock Text="Sort by:"
Name="SortText"
RelativePanel.AlignVerticalCenterWithPanel="True"
Margin="0,0,5,0"/>
<ComboBox RelativePanel.RightOf="SortText"
x:Name="MSortingBox"
ItemsSource="{Binding EnSortList}"
RelativePanel.AlignVerticalCenterWithPanel="True"
SelectionChanged="MSortingBox_SelectionChanged"
Width="120"/>
</RelativePanel>
<RelativePanel Margin="10,0">
<TextBlock Text="Country: "
Name="CountryText"
RelativePanel.AlignVerticalCenterWithPanel="True"
Margin="0,0,5,0"/>
<ComboBox RelativePanel.RightOf="CountryText"
x:Name="MCountryBox"
ItemsSource="{Binding EnCountryList}"
RelativePanel.AlignVerticalCenterWithPanel="True"
SelectionChanged="MCountryBox_SelectionChanged"
Width="120"/>
</RelativePanel>
</StackPanel>
Flyout control:
<Button>
<Button.Flyout>
<Flyout Placement="Left"
x:Name="MOptionsFlyout"
Content="{StaticResource PageOptionsFlyout}"
Opened="MOptionsFlyout_Opened">
</Flyout>
</Button.Flyout>
</Button>
If I understand your question correctly, you want to share the XAML for your Options layout between the main page and a flyout, based on the size of the page (for phone vs tablet). You can do this by creating a DataTemplate with the layout and adding it to the page's resource dictionary. Then it can be referenced in multiple places.
Here's the code below that does that. It also hides and shows the pieces based on adaptive triggers.
<Page.Resources>
<DataTemplate x:Key="PageOptionsTemplate">
<StackPanel
x:Name="PageOptionsPanel"
HorizontalAlignment="Right"
Orientation="Vertical">
<AppBarButton
Icon="Refresh"
Label="Refresh" />
<RelativePanel Margin="10,0">
<TextBlock
Name="SortText"
Margin="0,0,5,0"
RelativePanel.AlignVerticalCenterWithPanel="True"
Text="Sort by:" />
<ComboBox
x:Name="MSortingBox"
Width="120"
RelativePanel.AlignVerticalCenterWithPanel="True"
RelativePanel.RightOf="SortText"/>
</RelativePanel>
<RelativePanel Margin="10,0">
<TextBlock
Name="CountryText"
Margin="0,0,5,0"
RelativePanel.AlignVerticalCenterWithPanel="True"
Text="Country: " />
<ComboBox
x:Name="MCountryBox"
Width="120"
RelativePanel.AlignVerticalCenterWithPanel="True"
RelativePanel.RightOf="CountryText"
/>
</RelativePanel>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Name="OptionsFlyoutButton" Content="Show Me" Visibility="Collapsed">
<Button.Flyout>
<Flyout>
<ContentControl ContentTemplate="{StaticResource PageOptionsTemplate}"/>
</Flyout>
</Button.Flyout>
</Button>
<ContentControl Name="OptionsInLine" Visibility="Visible" ContentTemplate="{StaticResource PageOptionsTemplate}" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="320"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="OptionsInLine.Visibility" Value="Collapsed"/>
<Setter Target="OptionsFlyoutButton.Visibility" Value="Visible"/>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="720"/>
</VisualState.StateTriggers>
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="1024"/>
</VisualState.StateTriggers>
<VisualState.Setters>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
You can also move the DataTemplate out to the application level ResourceDictionary, so that it can be shared between multiple pages.
Finally, another option is to create a user control (using the uwp item template) for this. I recommend creating that if you needed more control over the layout, wanted to encapsulate the logic too, and share it across multiple apps.
For your example, the shared DataTemplate is the easiest path.
Just do this:
<Button Content="Show Me">
<Button.Flyout>
<Flyout>
<StackPanel
x:Name="PageOptionsPanel"
HorizontalAlignment="Right"
Orientation="Vertical">
<AppBarButton
Icon="Refresh"
Label="Refresh" />
<RelativePanel Margin="10,0">
<TextBlock
Name="SortText"
Margin="0,0,5,0"
RelativePanel.AlignVerticalCenterWithPanel="True"
Text="Sort by:" />
<ComboBox
x:Name="MSortingBox"
Width="120"
RelativePanel.AlignVerticalCenterWithPanel="True"
RelativePanel.RightOf="SortText"/>
</RelativePanel>
<RelativePanel Margin="10,0">
<TextBlock
Name="CountryText"
Margin="0,0,5,0"
RelativePanel.AlignVerticalCenterWithPanel="True"
Text="Country: " />
<ComboBox
x:Name="MCountryBox"
Width="120"
RelativePanel.AlignVerticalCenterWithPanel="True"
RelativePanel.RightOf="CountryText"
/>
</RelativePanel>
</StackPanel>
</Flyout>
</Button.Flyout>
</Button>
to get this:
When using the you get an auto display flyout that is shown whenever a user clicks the button, no code needed.
but to add content to that flyout, you need to have another element in it, then the stackpanel goes into it.
Hope this helps you.

how can i change the image in button inside in datatemplate wpf

how i say in title, i have a datatemplate for a Telerik RadTileView, in the large content i have a toolbar with a play button, the idea is that when a user click this button, the images in the tile view change automatically, i already do that but i need change the image inside the play button with a stop icon, this is my data template:
<DataTemplate x:Key="contentTemplate">
<telerik:RadFluidContentControl>
<telerik:RadFluidContentControl.Content>
<Border>
<Image Source="{Binding Frame}" />
</Border>
</telerik:RadFluidContentControl.Content>
<telerik:RadFluidContentControl.LargeContent>
<Grid>
<Grid>
<Image Source="{Binding Frame}" />
</Grid>
<Border BorderBrush="Black" BorderThickness="1" Background="#80000000" Height="80" VerticalAlignment="Bottom">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Style="{StaticResource BotonGrande}" Name="BotonImagenAtras" Click="BotonImagenAtras_Click">
<Image Style="{StaticResource ImagenGrande}" Source="/VisorSeproban;component/Imagenes/izquierda.png" />
</Button>
<Button Style="{StaticResource BotonGrande}" Name="BotonImagenesPlay" Click="BotonImagenesPlay_Click">
<Image Style="{StaticResource ImagenGrande}" Source="/VisorSeproban;component/Imagenes/play_on.png" />
</Button>
<Button Style="{StaticResource BotonGrande}" Name="BotonCaputarImagen" Click="BotonCaputarImagen_Click">
<Image Style="{StaticResource ImagenGrande}" Source="/VisorSeproban;component/Imagenes/captura_img_on.png" />
</Button>
<Button Style="{StaticResource BotonGrande}" Name="BotonImagenAdelante" Click="BotonImagenAdelante_Click">
<Image Style="{StaticResource ImagenGrande}" Source="/VisorSeproban;component/Imagenes/derecha.png" />
</Button>
</StackPanel>
</Border>
</Grid>
</telerik:RadFluidContentControl.LargeContent>
</telerik:RadFluidContentControl>
</DataTemplate>
Thanks for your help!
Regards
try attaching an event an change the image when the event get fire. you can try with Javascript if you are running your app in a browser.

Animate LongListSelectorItem Foreground on Hold in Windows Phone

I've been working my head a lot in the few past days on trying to acquire a nice foreground animation effect for the case of holding an item.
The Item template looks like this :
<DataTemplate>
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True" Hold="OnLongListSelectorItemHold">
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="edit" />
<toolkit:MenuItem Header="delete" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock x:Name="SubjectTextBlock" Text="{Binding Subject}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Last modified :" Margin="15, 0, 5, 0" Foreground="LightGray" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="{Binding LastModified}" Foreground="#989696" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
I have tried multiple approaches, but I haven't managed to get a result from any of them.
I came across this nice MSDN post which shows multiple examples, but none of them could be really match my case because, the TextBlock Foregrounds I want to animate, refer to TextBlocks, inside a DataTemplate so I have problems with accessing a specific control inside the template.
For example, I tried this approach :
<phone:PhoneApplicationPage.Resources>
<Storyboard x:Name="ItemHoldAnimation">
<ColorAnimation Storyboard.TargetName="SubjectTextBlock"
Storyboard.TargetProperty="Foreground"
From="White" To="{StaticResource PhoneAccentColor}" Duration="0:00:04"/>
</Storyboard>
</phone:PhoneApplicationPage.Resources>
And then to fire it from the Hold event handler :
var storyboard = Resources["ItemHoldAnimation"] as Storyboard;
storyboard.Begin();
But it fails because TargetName="SubjectTextBlock" is not accessible because it is inside the DataTemplate...
I have also tried an approach I found for WPF with EventTriggers, like this :
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True" Hold="OnLongListSelectorItemHold">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="StackPanel.Hold">
<BeginStoryboard Storyboard="{StaticResource ItemHoldAnimation}"/>
</EventTrigger>
</StackPanel.Triggers>
...
</StackPanel>
but it gives COM exception...
MS.Internal.WrappedException: Error HRESULT E_FAIL has been returned from a call to a COM component. ---> System.Exception: Error HRESULT E_FAIL has been returned from a call to a COM component.
A lot just to animate font when the LongListSelector item is hold...
What is the approach on solving this issue?
You should define the storyboard inside the DataTemplate, also the target proeprty need to be modified because ColorAnimation work on a Color property not a brush. Finally IsZoomEnabled="False" need also to be set because otherwise the ContextMenu implementation take a snapshot of the element and show this static image while the context menu is open so the animation will not be visible (the alternative is to modify the source code of ContextMenu to delay the opening of the context menu after your animation is done)
Something like this should work:
<DataTemplate x:Key="dataTemplate">
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True" Hold="OnLongListSelectorItemHold">
<StackPanel.Resources>
<Storyboard x:Name="ItemHoldAnimation">
<ColorAnimation Storyboard.TargetName="SubjectTextBlock"
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"
From="White" To="{StaticResource PhoneAccentColor}" Duration="0:00:04"/>
</Storyboard>
<Storyboard x:Name="MenuClosedAnimation">
<ColorAnimation Storyboard.TargetName="SubjectTextBlock"
Storyboard.TargetProperty="(TextBlock.Foreground).(SolidColorBrush.Color)"
From="{StaticResource PhoneAccentColor}" To="White" Duration="0:00:04"/>
</Storyboard>
</StackPanel.Resources>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu IsZoomEnabled="False" Closed="ContextMenu_OnClosed">
<toolkit:MenuItem Header="edit" />
<toolkit:MenuItem Header="delete" />
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock x:Name="SubjectTextBlock" Text="Test" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Last modified :" Margin="15, 0, 5, 0" Foreground="LightGray" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="{Binding LastModified}" Foreground="#989696" Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
and here is the hold method:
private void OnLongListSelectorItemHold(object sender, GestureEventArgs e)
{
FrameworkElement fe = sender as FrameworkElement;
var storyboard = fe.Resources["ItemHoldAnimation"] as Storyboard;
storyboard.Begin();
}
private void ContextMenu_OnClosed(object sender, RoutedEventArgs e)
{
ContextMenu eleme=sender as ContextMenu;
FrameworkElement fe = eleme.Owner as FrameworkElement;
var storyboard = fe.Resources["MenuClosedAnimation"] as Storyboard;
storyboard.Begin();
}

Categories