How to bind controls to different source from a datatemplate - c#

I have a simple design with several hubsections into which I want to display a ListBox inside a Grid. The ListBox itself will contain two elements : a TextBlock and a TextBox.
The TextBlock in each HubSection will display the same property, so the data binding is quite easy. However, the TextBox should display a different property in each separate HubSection. I am lost and do not even have a clue on how I should proceed.
Here is my XAML code :
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="BaseGridTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="12"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding PlayerName}"
FontSize="28"/>
<TextBox Text="{Binding OtherProperty, Mode=TwoWay}"
Background="Lavender" FontSize="28"
Grid.Column="2">
</TextBox>
</Grid>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
Then in my HubSections, I want to use the above template to display data, like such:
<HubSection Width="350" x:Uid="Test" Header="Test">
<DataTemplate>
<ListBox x:Name="TestListBox" Grid.Column="1"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BaseGridTemplate}"
>
</ListBox>
</DataTemplate>
</HubSection>
In my UI, both properties (PlayerName and OtherProperty) are displayed correctly. But what I would like to do is to bind to a different property for the TextBox (i.e. different from OtherProperty).
I really have no clue on how I should proceed, or if it even is possible.
I thought I could to something like defining my TextBox in the Resources section like such :
<TextBox Text="{Binding, Mode=TwoWay}">
And then Hope I could add something in the HubSection part ?

Assuming you have a way of knowing when you have to use your different properties I think the best way for you to do this is to use a template selector and have different kind of templates based on what you want to display.
Another alternative would be to use a value converter for TextBox and use the binding you suggested:
<TextBox Text="{Binding, Mode=TwoWay, Converter={StaticResource YourConverter}">
These are just some ideas to get you started, another important thing to know is if are you trying to use a different property on the same object?

Related

Binding textblock text from resources wpf

<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ElementName=txbValueY,Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
I try this above code but i cant to bind the text, how can i bind inside resources textblock text to outside the resources, Thanks
I am guessing that you are trying to show Text present in TextBlock resource in your second non-resource TextBlock.
You don't need DataTemplate. As you will progress ahead in WPF journey, you will come to know about those.
Below code will show "Resource Text" in your second TextBlock.
<Grid.Resources>
<TextBlock x:Key="TbRes1" Text="Resource Text" x:Name="txbValueY" Margin="5" FontSize="11" FontWeight="Medium"/>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource TbRes1},Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
All sorts of problems here:
You're specifying Mode.TwoWay in your TextBlock Text binding, it should be Mode.OneWay.
You're binding to the Label's Text property. Label doesn't have a Text property, only Content. And it's not a dependency property so you can't bind to it. (That said, a fluke of the internal mechanics does cause it to appear to work under certain conditions).
A template is exactly that: a template. You can't bind to something that doesn't exist, so the binding is meaningless.
Maybe you could clarify exactly what it is you're trying to do so we can suggest an alternative way of achieving it? Specifically, show us exactly how you're instantiating that DataTemplate.
UPDATE:
You need the first textbox to be created in order for the second one to bind to it, simply declaring it inside a DataTemplate doesn't cause that to happen by itself, so the direct binding will fail. Binding UI elements together like this should generally be avoided though, why can't you simply give the second textbox the same binding as the first?
<Grid>
<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ValueX}" Background="Orange" Foreground="White"/>
</Grid>
If for some reason this isn't possible then you can also create a binding proxy object (see this page for details):
<Grid>
<Grid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding ValueX}" />
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource ResourceKey=proxy}, Path=Data}" Background="Orange" Foreground="White"/>
</Grid>
Again, there are ways to bind to the data template declaration if you really want to, but to do that I'd have to see details of how your data template is being created at runtime.

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.

How to make TabItem template that contains same controls

I would like to make TabControl that contains TabItems. Within the TabItem, I would have StackPanel that contains various controls (ListBox, TextBox, Grid, etc). How do I make template of each TabItem and make multiple of them?
For example lets say I would like to make TabControl that contains tabs for Food category (Meat, Fruit, Vege, etc). I could make them individually by making TabItems with Name and Header. But instead I would like to just make one like following and use it as template as tab could increase.
<TabControl x:Name="TabControl" Margin="3,3,3,3" MinWidth="200">
<TabItem Name="FoodCategory1">
<!-- Contents of this does not really matter but its same thing for each "FoodCategory" -->
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition MinWidth="20"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBox x:Name="InputBox" Grid.Column="0" Margin="5,5,5,5"></TextBox>
<Button x:Name="AddButton" Grid.Column="1" HorizontalAlignment="Right" Margin="0,0,5,0" Width="20" Height="20"></Button>
<Button x:Name="RemoveButton" Grid.Column="2" HorizontalAlignment="Right" Margin="0,0,5,0" Width="20" Height="20"></Button>
</Grid>
<ListBox x:Name="FoodList" Margin="3,3,3,3" MinHeight="40"></ListBox>
</StackPanel>
<!-- example end -->
</TabItem>
</TabControl>
And of course from my code back I would like to access each individual tabitems content by FoodCategoryX.InputBox and so forth.
How should I approach this?
Any help would be appreciated.
Thank you.
Based upon what you wrote, you would write either a UserControl or CustomControl that acts as a container and provides the visual effects you are after. The simplest of these two is probably a UserControl. You would then populate each of your TabItems with an instance of your control and that enables your need to address the TabItems by name.
For your other question about addressing the various child controls by name, this is achievable, but a more robust approach would be to employ WPF's binding features by exposing dependency properties. Writing a UserControl is straight-forward effort and there's a working sample here

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}"/>

Get element inside a DataTemplate

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.

Categories