Get element inside a DataTemplate - c#

I have a following XAML and I need to change visibility of imageRemoveButton at runtime from the code behind file. How do I access that button?
<ItemsControl x:Name="ImagesItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#ffdddddd"
BorderThickness="0,0,0,1">
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="1"
VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"
TextWrapping="Wrap" />
</Grid>
<Button Grid.Column="3"
Width="20"
Height="20"
Content="X"
Template="{StaticResource ButtonAddTab}"
Style="{StaticResource ButtonWizard}"
Tag="{Binding}"
x:Name="imageRemoveButton"
Click="ImageRemoveButton_Click" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

Use the loaded event on the button to save a reference to the button in the cs file. Use this reference when needed to change the visibility.

You cannot reference-by-name elements in a template. There is no matching code-behind/designer property generated by templates.
You want to bind the visibility of a control in the template to a property of your data items. That means you will need to add a property to your data items. Can you provide more details of your data items? If the property is a bool value, use a VisibilityConvertor (dime a dozen on this site).
Think of templates as wanting to always pull settings from the bindings, rather than have settings stuffed into them from the outside.

Try using Source binding to a class that will manage the button's visibility state. Adding a property to all your data items would be painfull but could be required if the button's visibility needs to be set based on a property of your data item. As per standard MVVM patterns you can then control the button's visibility from C#. If you need a specific instance of the button you can add a Loaded event handler to it and cache it accordingly.

Related

How to change listview datatemplate in c# dynamically

I have a custom made item template for my listview with which values getting bound in .xaml itself.
I want to change one of the image dynamically in c# side.
I know in c# I can change in ContainerContentChanging but I am not able to access the custom templates image in c# please guide me out of this problem.
Here is my listview
<ListView ItemsSource="{Binding testList}" IsItemClickEnabled="True" ContainerContentChanging="ListView_ContainerContentChanging">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="2" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.80*"></ColumnDefinition>
<ColumnDefinition Width="0.20*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,8,0,8" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,8,0,4" Text="{Binding textVal1}" />
<Image Name="imgStatus" Source="ms-appx:///Assets/Icons/ic_test.png" Height="36" />
</StackPanel>
<TextBlock FontSize="18" Margin="0,0,0,4" Text="{Binding textVal2}" />
<TextBlock FontSize="21" Margin="0,0,0,0" Text="{Binding textVal3}" TextWrapping="WrapWholeWords" MaxLines="2" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I thought lets have the data template in a separate c# file ... but I am not able to link up the c# file inside the grid.
Any help to access the image from the data template is appreciated.
If you define the templates as a static resource, or elsewhere where they can be referenced in XAML, you can use a custom converter with appropriate bindings to handle the change in template.
However, I believe changing the ListView.ItemTemplate will affect all items in the collection. In order to change the template of individual items, you will have to add a wrapper object and modify that object's content (which you can do in a similar manner).
I understand you wished to change the template in C#, but I believe XAML's declarative style should be sufficient.

Programatically Update XAML Data Template in Windows 8 App

I have a data template like so:
<DataTemplate x:Key="itemTmpl">
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Height="90" Width="90" Background="#eee" />
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Id}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</Grid>
</DataTemplate>
I am setting a List View Item Source in the code behind like so:
lv.ItemTemplate = (DataTemplate)this.Resources["itemTmpl"];
How can I programatically change the Border Background before the template gets rendered to my UI?
You can do a few things:
Parse the Visual Tree (using a VisualTreeHelper or extension method such as from WinRTXamlToolkit), find the border (likely by its x:Name) and set the Background.
Bind the Background to a value in your Item
Bind the Background to a value in your VM
For any of the two Bindings, you can use a Converter to convert from a non-brush value to the proper brush value (such as if you wanted it to change colors based on if it were Checked, a bool, or not).
For the second binding, make sure that you set the Source to your VM, as the DataContext will be, by default, your Item. Also, make sure that your Item inherits from INotifyPropertyChanged so that your Bindings will get updated based on the changes in data properly.

WPF SettingsDialog using a ListView's SelectedItem and a ContentPresenter

I thought I cooked up something rather brilliant, but it's not working (yet!).
I am trying to get rid of all code behind in a SettingsDialog where a SettingsGroup can be selected by the user, which will consequently be visualized to the right of ListView (a similar settings dialog as Visual Studio has).
I was tipped to use a ContentPresenter, which I did, but then later realized that the tipper probably meant to rely on only XAML to get the job done.
This is what I got so far.
Declaring the SettingsGroups in XAML:
<Grid.Resources>
<w:DefinePathsUserControl x:Key="DefinePathSettingsGroup"></w:DefinePathsUserControl>
<w:HideShowTvShowsUserControl x:Key="HideShowTvShowsSettingsGroup"></w:HideShowTvShowsUserControl>
</Grid.Resources>
Add the left hand side SettingsGroup selector (a simple ListView):
<ListView
x:Name="SettingsGroupSelector"
Grid.Row="0"
Grid.Column="0">
<ListViewItem
x:Name="PathSetting"
Tag="{StaticResource DefinePathSettings}"
Content="Path"/>
<ListViewItem
x:Name="HideShowTvShowsSetting"
Tag="{StaticResource HideShowTvShowsSettings}"
Content="Hide/Show TV Shows"/>
</ListView>
And then I thought I could simply bind the ContentPresenter to the tag of the selected item in the ListView, like:
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding Source=SettingsGroupSelector, Path=SelectedItem.Tag}" />
Unfortunately for me, this does not show anything in my ContentPresenter. I also don't get any errors in my output window.
Who can help me further?
Change
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding Source=SettingsGroupSelector, Path=SelectedItem.Tag}" />
to this
<ContentPresenter
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2"
Content="{Binding ElementName=SettingsGroupSelector, Path=SelectedItem.Tag}" />
More information on specifying the Binding Source

How to access a value in a TextBlock?

I have a window with the following elements, and I'm trying to access the value contained in <TextBlock Name="armingValue" but in my .xaml.cs file it doesn't seem to be recognised.
What do I need to do to access the value?
<Window.Resources>
<DataTemplate DataType="{x:Type ArmingVM:ArmingItem}">
<CheckBox Margin="10,5" IsChecked="{Binding IsSet}" Content="{Binding Name}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type ArmingVM:ArmingBindingData}">
<DockPanel>
<ItemsControl ItemsSource="{Binding ArmingItems}" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock Text="Enum Value: " HorizontalAlignment="Right"/>
<TextBlock Name="armingValue" Text="{Binding Value}" HorizontalAlignment="Right"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="193*" />
<ColumnDefinition Width="551*" />
</Grid.ColumnDefinitions>
<Button Content="Get Panel Options" Name="btnGetOptionsConfigruation" Margin="12,12,23,396" Click="btnGetOptionsConfigruation_Click"></Button>
<StackPanel Grid.Column="1" Height="325" HorizontalAlignment="Left" Margin="68,43,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="438">
<ItemsControl Name="armingItemsControl" ItemsSource="{Binding}"/>
</StackPanel>
</Grid>
The backing variables generated by visual studio within the .xaml.cs file are only generated for certain circumstances. Any 'named' element within the body of a user control will have a generated backing variable. However, named elements within templates will not be generated. This is because Visual Studio has no way of knowing how your template will be used. For example, your template could be used by an ItemsControl to generate multiple template instances. What should be generated within .xaml.cs in that case?
You have two options:
Use binding, so that the state of your TextBlock.Text property is bound to a view model, so that you do not have to access the TextBlock element directly.
'walk' the visual tree to locate your TextBlock at runtime.
For (2), I would suggest using Linq-to-VisualTree, where you can find your TextBlock as follows:
TextBlock block = layoutRoot.Descendants<TextBlock>()
.Cast<TextBlock>().Where(tb => tb.Name="armingValue")
.Single();
You do not need to access TextBox value but its binded value.
So considering that you have in XAML
<TextBlock Name="armingValue" Text="{Binding Value}" HorizontalAlignment="Right"/>
You need to read a Value
Try always to avoid access UI elements directly in WPF, cause sometimes (not so rare cases) it becomes really tricky to find them if not imossible (I mean not guranteed way). Access a Data that stands behind them.
Maybe I did not get the point but why don't you create a binding to textbox and mark it as two way?
<TextBlock Text="Enum Value: " HorizontalAlignment="Right" Text="{Binding Value, Mode=TwoWay}"/>

How to group controls inside Grid for reuse in XAML?

In my application I have some controls that logically belongs together and is reused many places in different windows. The controls are always placed inside a grid.
Instead of copying the controls (and the code behind) each time I want to use them, I would like to define and maintain them in a single xaml file as a single UserControl.
I have this now:
<Grid>
<Grid.ColumnDefinitions>
[ColumnDefinitions...]
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
[RowDefinitions...]
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
[More controls..]
</Grid>
But I want to reuse this part:
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
as a single control - but how do I define the Grid.Column when using the control (somehow supplying it as a parameter)? - and how do I set the Grid.RowSpan value (eventhough the code is moved to a new xaml file, and not defined inside a grid)?
Any comments?
Make them into a separate usercontrol, then include that in your project.
If you're using Blend, it's really easy, just select all the controls, right click and Make into Usercontrol.
You could also make it into a resource.
Define it in a ResourceDictionary and include the dictionary the places you want to use it.
There is one catch - the resource dictionary returns the same instance everytime - so you have to add the attribute x:Shared="false".
But the wpf way is to figure out how you can do it with a DataTemplate :)

Categories