Error: The name 'tBox' does not exist in the current context - c#

Error: The name 'tBox' does not exist in the current context.
XAML:
<ItemsControl Name="itemsControl">
<ItemsControl.Template>
<ControlTemplate>
<WrapPenel>
<ItemsPresenter/>
</WrapPenel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Name="tBox" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
C#:
tBox.Background=Brushes.White; // Error: The name 'tBox' does not exist in the current context.
How to access control?

The TextBlock you named tBox is inside a DataTemplate. Controls inside a template are in a different name scope, so you can't access it in code-behind via its name. I'm not sure but you might get it via the ItemTemplate property and casting it to a TextBlock. Or you can add a property in your code-behind representing the background and use binding on the TextBlock's Background property. Hope this helps.

Set it on the TextBlock, in your DataTemplate:
<DataTemplate>
<TextBlock Name="tBox" Background="White" Text="{Binding Name}"></TextBlock>
</DataTemplate>
Or if you wish to only set the Background in certain conditions, consider using Triggers:
<DataTemplate>
<TextBlock Name="tBox" Text="{Binding Name}"></TextBlock>
<DataTemplate.Triggers>
<Trigger SourceName="tBox" Property="IsMouseOver" Value="True">
<Setter TargetName="tBox" Property="Background" Value="White" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
More information on how to use Triggers can be found here: A Guided Tour of WPF - Part 4 (Data Templates and Triggers)

I didn't try but maybe the answer here works:
Access a control from within a DataTemplate with its identifying name
to use something like :
var tbUserIcon= (TextBlock)checkBox.Template.FindName("tbUserIcon", checkBox);
But I think this way isn't convenient at all, especially if there's lots of controls have to do it this way, and it can't be checked by intellisense when writing code real time.

this.Background=Brushes.White; (assuming its code behind the control)?

Since Background is a dependency property, you will have to use
tBox.SetValue(BackgroundProperty, new SolidBrush(Color.White));

Related

ToolTipService.ToolTip not working on DataGridCell in UWP XAML

i'm trying to display a tooltip over a cell in a Windows Toolkit DataGrid in UWP XAML.
I'm able to show a static value by doing this:
<wct:DataGridTextColumn Width="200" Binding="{Binding ChargeInformationDto.Description, Mode=OneWay}" Header="Navn" Tag="Description">
<wct:DataGridTextColumn.CellStyle>
<Style TargetType="wct:DataGridCell">
<Setter Property="ToolTipService.ToolTip" Value="Tooltip text to show" />
</Style>
</wct:DataGridTextColumn.CellStyle>
</wct:DataGridTextColumn>
But with Bindings it does not show:
<wct:DataGridTextColumn Width="200" Binding="{Binding ChargeInformationDto.Description, Mode=OneWay}" Header="Navn" Tag="Description">
<wct:DataGridTextColumn.CellStyle>
<Style TargetType="wct:DataGridCell">
<Setter Property="ToolTipService.ToolTip" Value="{Binding ChargeInformationDto.Description, Mode=OneWay}" />
</Style>
</wct:DataGridTextColumn.CellStyle>
</wct:DataGridTextColumn>
Anyone managed to make this work?
In UWP XAML, you cannot use binding in Style. For specific instructions, please refer to this document:
Windows Presentation Foundation (WPF) and Microsoft Silverlight supported the ability to use a Binding expression to supply the Value for a Setter in a Style. The Windows Runtime doesn't support a Binding usage for Setter.Value (the Binding won't evaluate and the Setter has no effect, you won't get errors, but you won't get the desired result either).
As you can see in the source code of DataGridTextColumn, DataGridTextColumn will dynamically create a TextBlock and TextBox to display and edit content, so using TooltipService directly in the XAML to set Tooltip will not be attached to the newly created TextBlock, which is the reason of Tooltip cannot be displayed.
If you want to display Tooltip, then you can consider creating a DataGridTemplateColumn:
<controls:DataGridTemplateColumn Width="200"
Header="Navn" Tag="Description">
<controls:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ChargeInformationDto.Description, Mode=OneWay}" FontSize="20"
ToolTipService.ToolTip="{Binding ChargeInformationDto.Description, Mode=OneWay}"/>
</DataTemplate>
</controls:DataGridTemplateColumn.CellTemplate>
</controls:DataGridTemplateColumn>

Multi-stage binding from ViewModel to Custom-Control to controls on ControlTemplate not working

I have a MVVM project with a View and a ViewModel in its DataContext.
In this project I have a class ComboBoxCustom which inherits from ComboBox. I define some additional functionality in my ComboBoxCustom class.
To this ComboBoxCustom class I assign a control template to define its appearance.
The (simplified) style defining the (simplified) control template looks like:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem="{TemplateBinding SelectedItem}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which resides in a ResourceDictionary. The real control template has some additional features which are left out since they are not relevant for the question.
I use this ComboBoxCustom control in my View using:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject, Mode=TwoWay}"/>
The view is ok, and all items get loaded in the ComboBox which I can select.
The problem is that when I select a different item in the ComboBox, the property SelectedMyObject in my ViewModel does not get updated and consequently its setter is not called. Therefore, the (correct) information about the selected object is not available in my ViewModel.
When I use <ComboBox .../> (without the TextPropertyInComboBoxCustom property) instead of <lib:ComboBoxCustom .../> everything works just fine but then I don't have the additional functionality defined in ComboBoxMessage which I need.
Can anyone tell me what is wrong and how to fix this issue so I can use ComboBoxMessage in my view? Preferably without breaking the MVVM pattern.
Thank you!
Thanx to ASh's comment and information in this post.
The problem is that the TemplateBinding is one way. Therefore, all information from the ViewModel can get into the controls in the template. But not the other way around.
The solution is to specify a normal binding as:
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Whichs does about the same as a TemplateBinding but is two way.
The control template has become:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am not sure about the SelectedValue property though. With the template like this, it works both when I use the SelectedValue property or the SelectedItem property.
The Mode=TwoWay option can be omitted in the view since the default binding mode for SelectedItem is already two way. The view line becomes:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject}"/>
Bind SelectedValue to the property in ViewModel
SelectedValue="{Binding SelectedMyObject, Mode=TwoWay}"
in your lib:ComboBoxCustom

Hide tooltip if binding is null

Currently i've got the following code to show a tooltip.
<Border BorderBrush="Black"
BorderThickness="{Binding Border}"
Height="23"
Background="{Binding Color}">
<ToolTipService.ToolTip>
<TextBlock Text="{Binding TooltipInformation}" />
</ToolTipService.ToolTip>
This is presented in a ItemsControl with about 25 items. Only a few of these have a value set to TooltipInformation
If TooltipInforation is an empty string, it still shows the tooltipbox containing the textblock as a very small window (about 5px high and 20px wide). Even if I set the textblock visbility to collapsed.
Is there a way to completely remove the tooltip if the value of TooltipInformation is null or a empty string?
One way to hide an empty tooltip for all controls is to create a style in a resource dictionary that is included in your App.xaml.
This style sets the visibility to collapsed when the tooltip is an empty string or null:
<!-- Style to hide tool tips that have an empty content. -->
<Style TargetType="ToolTip">
<Style.Triggers>
<Trigger Property="Content"
Value="{x:Static sys:String.Empty}">
<Setter Property="Visibility"
Value="Collapsed" />
</Trigger>
<Trigger Property="Content"
Value="{x:Null}">
<Setter Property="Visibility"
Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
Also include sys namespace (for String.Empty):
xmlns:sys="clr-namespace:System;assembly=mscorlib"
One way you can do that is wrap the ToolTip in a Rectangle and give it a Transparent color. Then you just set the Visibility to Collapsed on this Rectangle.
Update:
<Border Background="#FFE45F5F">
<Grid>
<TextBlock Text="{Binding Property1}"/>
<Rectangle Fill="Transparent" Visibility="{Binding Property2, Converter={StaticResource BooleanToVisibilityConverter}}" ToolTipService.ToolTip="{Binding TooltipInformation}"/>
</Grid>
</Border>
This is a WPF answer (haven't tried it in Silverlight).
Use ToolTipService.IsEnabled, and bind it to the tooltip property. Then use a converter to convert the tooltip string to a bool.
For example, I have the following:
<TextBlock x:Name="textBlock" ToolTipService.IsEnabled="{Binding EntryToolTip, Converter={StaticResource StringNullOrEmptyToBoolConverter}}">
...
</TextBlock>
Or in code-behind
ToolTipService.SetIsEnabled(textBlock, false);
I was having the same issue as I was setting value to String.Empty. Setting it to null solves the problem.
WinRT/Windows 8 App XAML
If just using the default tooltip I would otherwise recommend either setting the bound value to null in the viewmodel or using a converter whenever the item is empty.
In my case I've got a:
public string Name { get; }
Bound using:
<TextBlock Text="{Binding Name}" TextTrimming="CharacterEllipsis" Tooltip="{Binding Name}" />
Where the idea is to show the full name in the tooltip if cut of due to lack of width. In my viewmodel I simply:
if (string.IsNullOrEmpty(Name)) Name = null;
At least in .Net 4.0 this will not show a tooltip for me.
Strangely, none of these answers worked in my case. A reply to the top answer alludes to it - if you're ToolTip is related to a TextBlock then that solution won't work. I have a TextBlock within a DataGridTemplateColumn.CellTemplate element, and I just bound the text directly to the ToolTip property of the TextBlock like this:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
ToolTip="{Binding Path=SomeTextProperty}"
Style="{StaticResource TextBlockOverflowStyle}"
Text="{Binding Path=SomeTextProperty, NotifyOnSourceUpdated=True}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
And I ended up getting the desired behavior (hidden tooltip when text is empty) "for free".
You could create a converter from string to bool that returns false if the string length is 0 and true otherwise, then bind ToolTip.Active to TooltipInformation with that converter.

WPF: Multiple content presenters in a custom control?

I'm trying to have a custom control that requires 2 or more areas of the XAML to be defined by a child control - that inherits from this control. I'm wondering if there's a way to define multiple contentpresenters and one which acts as the default content presenter
<MyControl>
<MyControl.MyContentPresenter2>
<Button Content="I am inside the second content presenter!"/>
</MyControl.MyContentPresenter2>
<Button Content="I am inside default content presenter" />
</MyControl>
Is this possible, how do I define this in the custom control's template?
The template can just bind the separate ContentPresenter instances like this (I've only set one property here but you'll likely want to set others):
<ContentPresenter Content="{TemplateBinding Content1}"/>
<ContentPresenter Content="{TemplateBinding Content2}"/>
The control itself should expose two properties for content and set the default using the ContentPropertyAttribute:
[ContentProperty("Content1")]
public class MyControl : Control
{
// dependency properties for Content1 and Content2
// you might also want Content1Template, Content2Template, Content1TemplateSelector, Content2TemplateSelector
}
You can use an "ItemsControl" with a custom template.
<ItemsControl>
<ItemsControl.Style>
<Style TargetType="ItemsControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items[0]}"/>
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items[1]}"/>
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items[2]}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.Style>
<TextBlock Text="Item 1"/>
<TextBlock Text="Item 2"/>
<TextBlock Text="Item 3"/>
</ItemsControl>
Here's another option that doesn't require making a custom control and is more typesafe than doing the ItemsControl thing (if type safety is something you want..perhaps not):
...Use an attached property!
Create an attached property of the appropriate type. We happened to need a text control so I did a string TextContent attached property. Then create a TemplateBinding to it from the template, and when instantiating in Xaml set it there as well. Works nicely.

Adding TabItems dynamically

I have a TabControl control
<TabControl Name="Farms_myVillages"
ItemsSource="{Binding Villages}">
</TabControl/>
In the code behind I add some tabs dynamically to the TabControl as follows:
foreach (Village vill in Villages)
{
TabItem tab = new TabItem();
tab.Header = vill.Name;
VillageUserControl c = new VillageUserControl();
c.DataContext = vill;
tab.Content = c;
Farms_myVillages.Items.Add(tab);
}
where VillageUserControl is a UserControl that deal with the specified village. This code works fine and it gets the expected results...
The problem is that I don't want this to be in the code behind but just in the xaml itself.
I try this:
<TabControl Name="Farms_myVillages"
ItemsSource="{Binding Villages}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Name}"/>
<Setter Property="Content">
<Setter.Value>
<u:VillageUserControl DataContext="{Binding}"/>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
After I run it, it throws an exception: "Specified element is already the logical child of another element. Disconnect it first."
Did I miss something? Please help me here...
You set the wrong thing, you should not modify the ItemContainerStyle but the TabControl.ItemTemplate for the header and TabControl.ContentTemplate for the content.
(The exception may have to do with the fact that in the style only one VillageUserControl is created, but the style applies to multiple tab items.)
Now it is working:
<TabControl Name="Farms_myVillages"
ItemsSource="{Binding Villages}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<u:VillageResources/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Your approach of not having this in code behind is right, instead of using ItemContainerStyle use ItemTemplate and ContentTemplate. You can have a look at this sample from Josh Smith for creating a tabs using Templates and Styles -
http://code.msdn.microsoft.com/mag200902MVVM/Release/ProjectReleases.aspx?ReleaseId=2026

Categories