Cannot Make Binding to ZIndex from Another Element in WPF - c#

I want to make binding to the ZIndex of another element in WPF in the same .xaml file but it does not work.
The element to be bound.
<Border
x:Name="BubbleTop"
CornerRadius="5"
Background="#EBF5EB"
Padding="8,6"
BorderBrush="LightGray"
BorderThickness="1"
Grid.ZIndex="3">
<ContentPresenter />
</Border>
The element who initial a binding.
<TextBlock
x:Name="statusText"
Margin="..."
Foreground="{Binding ElementName=BubbleTop, Path=Grid.ZIndex, Converter={StaticResource ToggleColorConverter}}"
FontWeight="Bold"
Text="..."/>
In the converter, it is set to change the Foreground color according to the ZIndex of the Border element.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int z = (int)value;
if (z == 3)
return "Red";
else
return "Blue";
}
But it does not work. Any hint?

The converter you have would work fine, except that the Path for your binding is wrong. When binding to an attached property, you have to put the path in parens for the path to be parsed correctly.
That said, I don't think a converter really makes much sense here. You can use styling to address a simple toggle like this. This allows you to keep more of the view logic in XAML.
For example:
<TextBlock
x:Name="statusText"
Margin="..."
FontWeight="Bold"
Text="...">
<TextBlock.Style>
<p:Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Blue"/>
<p:Style.Triggers>
<DataTrigger Binding="{Binding ElementName=BubbleTop, Path=(Grid.ZIndex)}" Value="3">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
</p:Style.Triggers>
</p:Style>
</TextBlock.Style>
</TextBlock>
(Note: you can omit the p: XML namespace for the <Style/> element. I include that only because the Stack Overflow code formatter gets confused when there's a plain <Style/> element in XML and won't format the XML correctly.)

Try this in your value converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int z = (int)value;
if (z == 3)
return Brushes.Red;
else
return Brushes.Blue;
}

Related

Binding Button IsEnabled depending of the ComboBox selection

I'm sure this is really easy, but I don't know how do it.
I have a ComboBox and a Button, and I need to have the Button enabled only if the ComboBox has an item selected, i.e. if in the ComboBox has no elements showing, then the Button must be disabled. How can I do this?
I have tried doing the following:
IsEnabled="{Binding ElementName=mycombobox, Path=SelectedIndex}"/>
But it does not work. I'm using Silverlight 5.
Thanks in advance
MSDN has something that may be of help to you here. It suggests you use a converter or a data trigger. I haven't tested this, but perhaps this will work?
<Window.Resources>
<Style x:Key="MyButtonStyle" TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SelectedItem, ElementName=comboBox1}" Value="{x:Null}">
<Setter Property="UIElement.IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ComboBox Name="comboBox1">
<ComboBoxItem>One</ComboBoxItem>
<ComboBoxItem>Two</ComboBoxItem>
<ComboBoxItem>Three</ComboBoxItem>
</ComboBox>
<Button Style="{StaticResource MyButtonStyle}" Name="myButton" Content="Push me"/>
</Grid>
EDIT
I am under the impression that Cyndy has already figured all this out, but for any future readers...
As noted in the comments, you cannot do DataTriggers in Silverlight. You'll need to do a converter. Here is another post that may help. In essence, you'll need to have your XAML set something like:
<Button Content="MyButton" IsEnabled="{Binding SelectedItem, ElementName=comboBox1, Converter={StaticResource myConverter}}"/>
and then in your code-behind, you'll need something like:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(value == null);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
There might be a more efficient way to do it, but I'd simply determine whether the ComboBox.SelectedItem is null in the SelectedIndexChanged event.

Automatic tooltip when text is trimmed in DataGrid

My application runs with C# and WPF (.net framework 4.0). My goal is to have a DataGrid in which the text in the cells is trimmed with an ellipsis, and automatically has a tooltip with the full text displayed only if the text in the cell is actually trimmed.
Solution 1: I'm currently using this to know if the text is trimmed or not: http://tranxcoder.wordpress.com/2008/10/12/customizing-lookful-wpf-controls-take-2/
The problem is that it only works when I resize the columns. The tooltips don't show up when the DataGrid is first loaded, when the columns are sorted, or when the ItemSource of the DataGrid is updated.
Solution 2: I've also tried this:http://www.scottlogic.com/blog/2011/01/31/automatically-showing-tooltips-on-a-trimmed-textblock-silverlight-wpf.html
But the tooltips never appear on my DataGrid cells, while it works fine with isolated textblocks.
I'm looking for simple ways to improve Solution 1 and make it work in my DataGrid in all cases, or maybe a different approach.
The style for Solution 1:
<UserControl.Resources>
<Style x:Key="TextColumnElementStyle" TargetType="TextBlock" BasedOn="{StaticResource TextBlockService}">
<Style.Setters>
<Setter Property="TextWrapping" Value="NoWrap" />
<Setter Property="TextTrimming" Value="WordEllipsis" />
</Style.Setters>
</Style>
</UserControl.Resources>
The source code of the TextBlockService
The DataGrid for Solution 1:
<DataGrid ItemsSource="{Binding IssueList}" tbs:TextBlockService.AutomaticToolTipEnabled="True">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Description}"
ElementStyle="{StaticResource TextColumnElementStyle}">
</DataGrid.Columns>
</DataGrid>
Thanks
I've found the perfect solution, based on an answer by xr280xr.
It works out of the box, in any condition, and without using additional code.
The style, that I put in <DataGrid.Resources> :
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}" TextTrimming="CharacterEllipsis">
<TextBlock.ToolTip>
<ToolTip Visibility="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget, Converter={StaticResource TrimToVisConverter}}">
<ToolTip.Content>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}"/>
</ToolTip.Content>
</ToolTip>
</TextBlock.ToolTip>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The source of Converter={StaticResource TrimToVisConverter}:
public class TrimmedTextBlockVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null) return Visibility.Collapsed;
FrameworkElement textBlock = (FrameworkElement)value;
textBlock.Measure(new System.Windows.Size(Double.PositiveInfinity, Double.PositiveInfinity));
if (((FrameworkElement)value).ActualWidth < ((FrameworkElement)value).DesiredSize.Width)
return Visibility.Visible;
else
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I can't comment yet, I lack points needed for it.
But I just want to add an extra tip to the accepted answer code, for noobs like me who want to use it.
If you want to link your Converter to its code, make sure to create a link in your Window/UserControl Resources as mentioned below:
<Window.Resources>
<local:TrimmedTextBlockVisibilityConverter x:Key="TrimToVisConverter" />
</Window.Resources>
Additional thought on accepted answer: You also have to rewrite any style that comes with default DataGridCell template as DataGridCell States. Otherwise you lose clean view on row select and other stuff...

ValueConversion only when not selected

I have a TextBox which displays a file path to the user. The user can either use a OpenFileDialog to pick the file, which updates the text, or directly write/paste the path into the text box.
However, the text box is constrained in size and to avoid the path being cut off I'm using an custom IValueConverter to cut off part of the path to make sure both the drive letter, and the file name is visible.
For example,
C:\Users\USERNAME\Documents\CompanyName\ExportType\ExportName\Exportfile1.bin
becomes
C:\...\ExportType\ExportName\Exportfile1.bin
The problem occurs then when the user selects the field. As expected, the ConvertBack method of the IValueConverter is triggered, with the value now being the shortened path.
Is it then possible, without creating some custom intricate custom control, to show one formatted value when it's not selected, and the raw value when selected?
(I have access to the Telerik UI components, if it's already such a component there)
I came up with solution without code behind
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Text">
<Setter.Value>
<Binding Path="nazwa" Converter="{StaticResource converter}" ConverterParameter="false"/>
</Setter.Value>
</Setter>
<Setter Property="Foreground" Value="Red"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Foreground" Value="Gold"/>
<Setter Property="Text">
<Setter.Value>
<Binding Path="nazwa" Converter="{StaticResource converter}" ConverterParameter="true"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Converter
public class conv : IValueConverter
{
private string track = null;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
track = value.ToString();
return parameter.ToString().Equals("true") ? track: track.Substring(0,2);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Result when textbox is not selected
and when I select
just changing color yet, bytheway. The trick was to change parameters sending to converter. Let me know whether it works.

Is it possible to pass a parameter (a Binding) to a WPF Style

I have a WPF style that I am applying to list box items. Currently, each item is bound to a KeyValuePair. I display the Key in a TextBlock inside the ListBoxItem:
<TextBlock Name="Label" Text="{Binding Key}" />
What I want to do is make the Style generic so that if the data is not a KeyValuePair, (maybe just a string), I can bind the data correctly to the TextBlock.
Is there a way to pass a parameter to a Style or DataTemplate or make the data binding generic?
My style:
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="Border" Padding="2" SnapsToDevicePixels="true" CornerRadius="4" BorderThickness="1">
<TextBlock Name="Label" Text="{Binding Key}" />
</Border>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
</MultiTrigger.Conditions>
<Setter TargetName="Border" Property="Background" Value="{StaticResource SelectionGradient}" />
</MultiTrigger>
<ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Let me give you a quick example. Let's say your TextBox contains a number, and you want it to be with a red Background if the number is negative, or with a green background if >= 0
Here is the style you'd write:
<TextBlock Text="{Binding Key}" >
<TextBlock.Style>
<Style TargetType="TextBox">
<Setter Property="Background" Value="{Binding Key, Converter={StaticResource MyConverterResource}" />
</Style>
</TextBlock.Style>
</TextBlock>
Here is the converter you'd write:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double source = (double)value;
return source < 0 ? Brushes.Red : Brushes.Green;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// We don't care about this one here
throw new NotImplementedException();
}
}
And, to access the converter in your xaml, you just have to do the following, assuming your converter is in the namespace MyNamespace:
<Window xmlns:my="clr-namespace:MyNamespace">
<Window.Resources>
<my:MyConverter x:Key="MyConverterResource">
</Window.Resources?
<!-- Your XAML here -->
</Window>
(of course you can put this in any Resources, may it be a UserControl or whatever )
This will allow you to call your converter by writing {StaticResource MyConverterResource}
And here, you'd have a conditional style, the converter deciding which color to set as a background according to one parameter, in my case, the value itself (but it can be whatever you want)

Expander grouping collapses after source reset

I have a listbox with a bit of templating. Groups are represented by expanders. The listbox is linked to the filesystem and each folder gets its own expander. Any time a file is renamed, deleted, etc, the listbox's view is refreshed. This works great but once the refresh is called, each of the expanders collapses. I can't seem to find a good way to keep them open. I saw another question that used binding to solve this for a single expander. The issue with a data binding on the "IsExpanded" is that there are an unknown number of expanders and I have no way of knowing how many there will be, what they will be called, etc at design time. Any ideas?
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander VerticalAlignment="Top"
OverridesDefaultStyle="True"
Template="{StaticResource SimpleExpanderTemp}">
<Expander.Header>
<TextBlock VerticalAlignment="Center"
Background="Transparent"
Text="{Binding Path=Name}"
FontFamily="SegoeUI"
FontSize="16"
Foreground="Black"/>
</Expander.Header>
<Expander.Tag>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="#696969" />
<GradientStop Offset="1.0" Color="#474747" />
</LinearGradientBrush>
</Expander.Tag>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListBox.GroupStyle>
One possible solution is to still use data binding on the IsExpanded property.
Instead of binding to a boolean, bind to a list of booleans and use a ValueConverter to retrieve the appropriate item from the list.
When creating all your expanders, give each one an index number, if you're not already. Then when you bind the IsExpanded property, set the Converter, and set the converter parameter to the index number of the expander. Then your converter will receive the list of boolean values as the 'value' argument and the index number as the 'parameter' argument and your converter can then return a boolean value.
Your converter might look like this:
public class ListToBooleanConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((value != null) & (parameter != null)) {
try {
Int16 itmNum = Convert.ToInt32(parameter);
List<bool> lst = value;
return lst[itmNum];
} catch (Exception ex) {
return null;
}
}
return null;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This method or operation is not implemented.");
}
}
In XAML, the implementation of this data binding and converter would look like this (for the expander with an index number of 5):
IsExpanded="{Binding Path=ListIsExpanded, Converter={StaticResource ListToBooleanConverter}, ConverterParameter=5}">
Obviously, in code, this implementation will look a little different.

Categories