I'm using a ComboBox (WPF 4.0) to show user defined paragraph styles for an editor app. The ComboBox has two columns:
(1) Name of the paragraph style
(2) Text "abcABC123", should in some properties be formatted according to the paragraph style in the first column
(1) is working, (2) is not because _ResourceKey_background, _ResourceKey_foreground and _ResourceKey_fontFamily are no ResourceKeys
but variables containing ResourceKeys. How can I solve this?
_NameInternal, _NameUI, _ResourceKey_background, _ResourceKey_foreground and _ResourceKey_fontFamily are public properties
of the user defined paragraph style class.
<ComboBox Name="_cbStylesPara" SelectedValuePath="_NameInternal"
ItemsSource="{Binding Source={StaticResource _collectionViewSource_stylesPara}}" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Column="0" Text="{Binding _NameUI}" VerticalAlignment="Center" />
<TextBlock Grid.Column="1" Text="abcABC123" Margin="3,0,0,0" VerticalAlignment="Center"
Background="{DynamicResource _ResourceKey_background}"
Foreground="{DynamicResource _ResourceKey_foreground}"
FontFamily="{DynamicResource _ResourceKey_fontFamily}" />
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
As like you said resources are declared under App resources, what you can do is create an IValueConverter and return the resource value from it's Convert method.
public class ResouceLookupConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return App.Current.TryFindResource(value);
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
XAML:
<ComboBox>
<ComboBox.Resources>
<local:ResouceLookupConverter x:Key="ResouceLookupConverter"/>
</ComboBox.Resources>
......
<TextBlock Grid.Column="1" Text="abcABC123" Margin="3,0,0,0"
VerticalAlignment="Center"
Background="{Binding _ResourceKey_background,
Converter={StaticResource ResouceLookupConverter}}"
Foreground="{Binding _ResourceKey_foreground,
Converter={StaticResource ResouceLookupConverter}}"
FontFamily="{Binding _ResourceKey_fontFamily,
Converter={StaticResource ResouceLookupConverter}}" />
</ComboBox>
Note: Ofcourse you have to define local namespace in your XAML set to the namespace where your Converter is declared.
Related
I have a Listbox which consists of elements that looks like this.
class ItemForListbox
{
public string path { get; set; }
public string file { get; set; }
public InstallationPackageChoice choices { get; set; }
public KeepKill keepKill { get; set; }
}
The choices and keepKill are objects.
My XAML for the listbox has this DataTemplate
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Name="btnPackageVersion" Style="{StaticResource ListBoxButtonFormat}" Content ="Package version" Click="btnInstallPackage_Click" HorizontalAlignment="Right"/>
<TextBlock Name="txtBlkPath" Text="{Binding path}" FontSize="10" Width="500" Height="20" Margin="10,10,0,0"/>
<Button Name="btnFilterPath" Style="{StaticResource ListBoxButtonFormat}" Content ="Filter path" Click="btnFilterpath_Click" HorizontalAlignment="Right"/>
<TextBlock Name="txtBlkFile" Text="{Binding file}" FontSize="10" Width="150" Height="20" Margin="10,10,0,0"/>
<Button Name="btnFilterfile" Style="{StaticResource ListBoxButtonFormat}" Content="Filter file" Click="btnFilterfile_Click" HorizontalAlignment="Right"/>
<CheckBox Name="chkBxKeep" Content="Keep" VerticalAlignment="Center" HorizontalAlignment="Right" IsChecked="True" Checked="chkBxKeep_Checked"/>
<CheckBox Name="chkBxKill" Content="Kill" VerticalAlignment="Center" HorizontalAlignment="Right" Checked="chkBxKill_Checked"/>
<Separator Name="MySeparator"
Height="3"
Width="Auto"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="Red" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
The two checkboxes underlying data is the killKeep object in the Listbox element object.
The problem is that i would like to have a radiobutton functionality of the two checkboxes, so when i check a checkbox, the other one unchecks. The reason i dont use radiobuttons are that i simply do not understand them in regards to binding. But now i find i also do not know how to do this binding with the checkboxes.
I hope someone can help me.
You can use binding with a converter to bind IsChecked property of checkboxes:
<Window.Resources>
<local:InverseBooleanConverter x:Key="InverseBooleanConverter"/>
</Window.Resources>
...
<CheckBox Name="chkBxKeep" Content="Keep" VerticalAlignment="Center" HorizontalAlignment="Right" IsChecked="True" Checked="chkBxKeep_Checked"/>
<CheckBox Name="chkBxKill" Content="Kill" VerticalAlignment="Center" HorizontalAlignment="Right" Checked="chkBxKill_Checked"
IsChecked="{Binding ElementName=chkBxKeep, Path=IsChecked, Converter={StaticResource InverseBooleanConverter}}"/>
Converter
public class InverseBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && value is bool)
{
return !(bool)value;
}
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Convert(value, targetType, parameter, culture);
}
#endregion
}
I am trying to make a scroll viewer in which scroll button is visible only when window size is too small to show all the content. I have put some button into a stackpanel which is in the scrollviewer.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
<RowDefinition/>
</Grid.RowDefinitions>
<RepeatButton x:Name="LineLeftButton"
Grid.Column="0"
Grid.Row="0"
Command="{x:Static ScrollBar.LineDownCommand}"
Background="{StaticResource uparrow}"
CommandTarget="{Binding ElementName=scrollViewer}"/>
<RepeatButton x:Name="LineRightButton"
Grid.Column="0"
Grid.Row="2"
Background="{StaticResource downarrow}"
Command="{x:Static ScrollBar.LineUpCommand}"
CommandTarget="{Binding ElementName=scrollViewer}"/>
<ScrollViewer Grid.Column="1" Grid.Row="1" x:Name="scrollViewer"
VerticalScrollBarVisibility="Hidden"
HorizontalScrollBarVisibility="Hidden">
<StackPanel Orientation="Vertical">
<Button Width="150" Height="20"/>
<Button Width="150" Height="20"/>
<Button Width="150" Height="20"/>
<Button Width="150" Height="20"/>
<Button Width="150" Height="20"/>
<Button Width="150" Height="20"/>
<Button Width="150" Height="20"/>
</StackPanel>
</ScrollViewer>
</Grid>
The scroll is working kk but how do you disable it when there are not enough element in the stack such that a scroll is not required?
You can create a converter like the following
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double)
{
return ((double)value < 140) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
where 140 is the total height needed (you can make this parameter dynamic). Then you just need to bind the visibility of your button
<RepeatButton x:Name="LineRightButton"
Visibility="{Binding ActualHeight, Converter={StaticResource MyConv}, ElementName=scrollViewer, Mode=OneWay}"
Grid.Column="0"
Grid.Row="2"
Background="{StaticResource downarrow}"
Command="{x:Static ScrollBar.LineUpCommand}"
CommandTarget="{Binding ElementName=scrollViewer}"/>
You just need to define the converter mentioned above in your resources and it's done
EDIT Solution number 2 with Multibinding:
if you want to avoid to hardcode the total height of your button you can use a multivalueconverter and a multibinding
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
{
if (value[0] is double && value[1] is double)
{
return ((double)value[0] < (double)value[1]) ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and modify your button in this way
<RepeatButton x:Name="LineRightButton"
Grid.Column="0"
Grid.Row="2"
Background="Black"
Command="{x:Static ScrollBar.LineUpCommand}"
CommandTarget="{Binding ElementName=scrollViewer}">
<RepeatButton.Visibility>
<MultiBinding Converter="{StaticResource MyConv}">
<Binding Path="ActualHeight" ElementName="scrollViewer"/>
<Binding Path="ActualHeight" ElementName="test"/>
</MultiBinding>
</RepeatButton.Visibility>
</RepeatButton>
where "test is the name of the stackpanel containing the buttons
The context is I have a Listbox that is outputting items with quite a lot of styling (more than i've put here)... I went to use a DataTemplateSelector to show either one image or another based on a condition but it was stupid to have to re-write the whole tempalte with just this one difference in it.
I have placed some pseudo code to demonstrate what i'm trying to achieve:
<ListBox Grid.Row="1"
ItemsSource="{Binding TestCases}"
BorderThickness="0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="10"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Padding="0,5,0,5">
<Run Text="{Binding Class}"></Run>
</TextBlock>
<TextBlock Grid.Column="1"
Padding="5">
<Run Text="{Binding Assertions}"></Run>
</TextBlock>
if {Binding Failures} > 0 {
<Image HorizontalAlignment="Center"
Width="10"
Height="10"
Grid.Column="2"
Source="/Content/Images/Cross.png">
</Image>
}
else
{
<Image HorizontalAlignment="Center"
Width="10"
Height="10"
Grid.Column="2"
Source="/Content/Images/Tick.png">
</Image>
}
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any ideas on how i do it?
-------------Edit------------
So far i've added the namespace of my converter class to the XAML file:
xmlns:converters="clr-namespace:Blah.Blah.Converters"
Then added window resources:
<Window.Resources>
<converters:FailuresTickConverter x:Key="failuresTickConverter" />
<converters:FailuresCrossConverter x:Key="failuresCrossConverter" />
</Window.Resources>
Then i have the classes themselves:
namespace Blah.Blah.Converters
{
public class FailuresTickConverter : IValueConverter
{
public object Convert( object value, Type targetType,
object parameter, CultureInfo culture )
{
int failures = (int)value;
if( failures > 0 )
return Visibility.Hidden;
return Visibility.Visible;
}
public object ConvertBack( object value, Type targetType,
object parameter, CultureInfo culture )
{
throw new NotImplementedException();
}
}
public class FailuresCrossConverter : IValueConverter
{
public object Convert( object value, Type targetType,
object parameter, CultureInfo culture )
{
int failures = ( int )value;
if( failures > 0 )
return Visibility.Visible;
return Visibility.Hidden;
}
public object ConvertBack( object value, Type targetType,
object parameter, CultureInfo culture )
{
throw new NotImplementedException();
}
}
}
Then in my XAML on the images i've done this:
<Image Visibility="{Binding Failures, Converter=failuresTickConverter}"
HorizontalAlignment="Center"
Width="10"
Height="10"
Grid.Column="2"
Grid.Row="0"
Source="/Content/Images/Tick.png">
</Image>
<Image Visibility="{Binding Failures, Converter=failuresCrossConverter}"
HorizontalAlignment="Center"
Width="10"
Height="10"
Grid.Column="2"
Grid.Row="0"
Source="/Content/Images/Cross.png">
</Image>
I'm getting an error on the bindings:
IValueConverter does not support converting from string
Elaborating on my comment, here's a simple (not the only one, of course) way to do it:
Create a converter to convert number to visibility:
public class PositiveToVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (int)value > 0 ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add the namespace of the converter to your xaml, add the converter to resources (use your own namespace of course):
<Window ...
xmlns:converters="clr-namespace:WpfApplication1.Converters"
...>
<Window.Resources>
<converters:PositiveToVisibilityConverter x:Key="PositiveConverter"/>
</Window.Resources>
Then put both the images in, the "success" one first and bind the property using the converter to the "failure" one:
<Image HorizontalAlignment="Center"
Width="10"
Height="10"
Grid.Column="2"
Source="/Content/Images/Tick.png"
Visibility="{Binding Failures, Converter={StaticResource PositiveConverter}}">
</Image>
<Image HorizontalAlignment="Center"
Width="10"
Height="10"
Grid.Column="2"
Source="/Content/Images/Cross.png">
</Image>
Since these images are in a grid (I assume) and have the same position and size, they will overlap and normally the one that's defined last will be drawn last, so the first one will not be visible. That is, unless the binding makes the second image invisible.
<ListView ItemsSource="{Binding Collection}"
SelectedValue="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="Auto"
Width="Auto"
VerticalAlignment="Bottom" HorizontalAlignment="Left">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Aqua" BorderThickness="2">
<StackPanel Width="Auto"
Height="Auto"
Background="Blue">
<TextBlock Text="{Binding colId, Converter={StaticResource StringFormatConverter}, ConverterParameter='Collection ID: {0}'}"/>
<TextBlock Text="{Binding Description, Converter={StaticResource StringFormatConverter}, ConverterParameter='Description: {0}'}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C# Code String format converter class
public sealed class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
The above class should take care of the formatting that I want to display in the UI, but I am getting 'The resource could not be resolved'.
In your page/user control xaml which contains the listview, you have define the convertor as a static resource before you can use it. So if your Convertor is in namespace dummynamespace then place this in either :
<Page xmlns:common="using:<dummynamespace>">
<Page.Resources>
<common:StringFormatConverter x:Key="StringFormatConverter" />
</Page.Resources>
or
<ListView ItemsSource="{Binding Collection}"
SelectedValue="{Binding Selected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="Auto"
Width="Auto"
VerticalAlignment="Bottom" HorizontalAlignment="Left">
<ListView.Resources>
<common:StringFormatConverter x:Key="StringFormatConverter" />
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Aqua" BorderThickness="2">
<StackPanel Width="Auto"
Height="Auto"
Background="Blue">
<TextBlock Text="{Binding colId, Converter={StaticResource StringFormatConverter}, ConverterParameter='Collection ID: {0}'}"/>
<TextBlock Text="{Binding Description, Converter={StaticResource StringFormatConverter}, ConverterParameter='Description: {0}'}"/>
</StackPanel>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I have a data template with a TexBlock in XAML. This TexBlock shows a word in a word list. Every word I want to put the first letter capitalized, because all words are in lowercase.
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="AddrBookItemTemplate">
<StackPanel VerticalAlignment="Top">
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name}" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
In c# implement the converter
namespace Converter.ViewModels
{
public class ToCapitalizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as string).ToLower();
}
}
}
In App.xaml
...
xmlns:vm="clr-namespace:Converter.ViewModels"
<Application.Resources>
<vm:ToCapitalizeConverter x:Key="ToCapitalizeConverter"/>
</Application>
In MainPage.xaml
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="AddrBookItemTemplate">
<StackPanel VerticalAlignment="Top">
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name, Converter={StaticResource ToCapitalizeConverter}}" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
You can use a converter as follows:
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name, Converter ={StaticResource myConverter}}" />
Specific information on how to implement a converter can be found here. You can essentially perform any operation you like on the text. I actually like Humanizer to do these type of text conversions.