I am creating a custom WPF control that let's say for simplicity sake has a vertical stack panel with a "title" TextBlock, followed by a ContentPresenter. I want the font size for the "title" to be 5 Points LARGER than the size used in the content, which is inherited by whatever container the user places this control in.
How can I specify a font size in the control template for the header element using a relative value without exposing a property like "TitleFontSize" to the user? I want do "add 5".
I tried using a ScaleTransform on the header text block with mixed results (the text block scaled fine but the orientation was modified - I had the text right-justified and it moved "off the control" area when scaled). Also, I am not sure if scale transform would be approprite here.
A more generic way
Value converter
public class MathConverter : IValueConverter
{
public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
return (double)value + double.Parse( parameter.ToString() );
}
public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
{
return null;
}
}
Converter Resource
<my:MathConverter x:Key="MathConverter" />
XAML
<TextBlock FontSize="{Binding
RelativeSource={RelativeSource AncestorType={x:Type Window}},
Path=FontSize,
Converter={StaticResource MathConverter},
ConverterParameter=2}" />
I did it with an IValueConverter as follows:
Created a class FontSizeConverter that derives from IValueConverter. The Convert method adds 10 to the value, and the ConvertBack method subtracts 10.
public class FontSizeConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (double)value + 12.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (double)value - 12.0;
}
#endregion
}
Next, I declaried an instance of this class in the XAML template for the control:
<Style.Resources>
<local:FontSizeConverter x:Key="fontSizeConverter"/>
</Style.Resources>
And Finnaly, the FontSize binding uses this converter applied to the inherited FontSize property:
<TextBlock FontSize="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=FontSize, Converter={StaticResource fontSizeConverter}}"
Grid.Row="0" Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Date.Day}" HorizontalAlignment="Right" VerticalAlignment="Top" Padding="2" Margin="2" >
</TextBlock>
This works. But I still do not know if this is the correct answer. Let me know if there is a better way, or if this is appropriate.
Related
So i have this TextBlock:
<TextBlock
Name="tbVersion"
Text="{Binding Converter={StaticResource TextConverter}}"/>
Converter:
public class TextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return "bla bla";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And i got this error when try to run my application:
{"Cannot find resource named 'TextConverter'. Resource names are case
sensitive."}
Although there is no compiler errors at all and i declare this:
<Window.Resources>
<Convertors:TextConverter x:Key="TextConverter"/>
</Window.Resources>
Ok so i try to take another Converter that works from another TextBlobk and still got this error that cannot find resource although this exist (and works as i mentioned...) so maybe because my TextBlobk is inside DataTemplate :
<Controls:MetroWindow.TitleTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock
Name="tbVersion"
Text="{Binding Converter={StaticResource TextConverter}}"/>
</StackPanel>
</DataTemplate>
</Controls:MetroWindow.TitleTemplate>
As you can see from the image, i used exactly your code, and is working just fine.
So you may have added the converter to Window.Resources but you are using it in a different ResourceDictionary?
If you want to make them globally usable you should add them to the App.xaml, or programmatically to the Application.Current.ResourceDictionary
EDIT:
Try adding the converter to the DataTemplate.Resources
I'm implementing an ObservableCollection <-> DataGrid binding.
My class consists of a System.Windows.Media.Color field (named 'Color') and several strings.
My implementation in WPF of the Color column:
<DataGridTemplateColumn Header="Color" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource ColorProperties}}"
SelectedItem="{Binding Path=Color, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource ComboBoxFlatStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="1" Orientation="Horizontal">
<Rectangle Fill="{Binding}" Height="10" Width="10" Margin="2"/>
<TextBlock Text="{Binding}" Margin="2,0,0,0"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
And ColorProperties:
<ObjectDataProvider x:Key="ColorProperties" ObjectType="{x:Type color:ColorHelper}"
MethodName="GetColorNames"/>
The ColorHelper class (I'll try to find the OP for credit):
public static class ColorHelper
{
public static IEnumerable<string> GetColorNames()
{
foreach (PropertyInfo p
in typeof(Colors).GetProperties(
BindingFlags.Public | BindingFlags.Static))
{
yield return p.Name;
}
}
}
This works just fine, I can see the combo-box with all the colors:
Now I added a few objects to the ObservableCollection, and their respective fields are populated in the DataGrid. All except the Color.
The default Color is Black, but when I run the application, the ComboBox stays empty.
When I select a color from the ComboBox, the ObservableCollection changes accordingly. However if I change the object itself, the ComboBox stays with its original value.
For troubleshooting, I added
<DataGridTextColumn Header="Color" Binding="{Binding Color, UpdateSourceTrigger=LostFocus}"
Width="100"/>
This column gets populated with the string representation of the color, and when I change the value from either side, it changes accordingly.
This fact suggests that there's something missing in the ComboBox implementation.
Am I missing some sort of translation method? Seems weird since it actually works in one way (from ComboBox to object).
I've searched everywhere for a solution, without success.
EDIT:
I implemented Color<->String converter, still doesn't work..
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color content = (Color)value;
return content.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string content = (string)value;
return (Color)ColorConverter.ConvertFromString(content);
}
}
WPF change:
<ComboBox ItemsSource="{Binding Source={StaticResource ColorProperties}}"
SelectedItem="{Binding Path=Color, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,
Converter={StaticResource StringToColorConverter}}"
Style="{StaticResource ComboBoxFlatStyle}">
Any ideas?
Thanks!
The list bound to Combobox is a list of strings, whereas the actual object is not just a string. you need a converter that converts from string to your desired object.
Rest all seems fine, system dosent know how does string Black translates to Color Black
-- one way binding works because, color object knows how to get color from a string, that is why when we set Background="String" it get the corresponding object using FromName
OK i found out what was wrong. It looks like getting the actual name from a color is not that simple. ToString returns the hex string, and not the actual color name.
So I used the answer from http://www.blogs.intuidev.com/post/2010/02/05/ColorHelper.aspx to create the converter.
public class ColorToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color KnownColor;
Color content = (Color)value;
Type ColorType = typeof(System.Windows.Media.Colors);
PropertyInfo[] ColorsCollection = ColorType.GetProperties(BindingFlags.Public | BindingFlags.Static);
foreach (PropertyInfo pi in ColorsCollection)
{
KnownColor = (Color)pi.GetValue(null);
if (KnownColor == content)
return pi.Name;
}
return String.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
string content = (string)value;
return (Color)ColorConverter.ConvertFromString(content);
}
}
I have a list of objects that I'm binding to the screen. One of the properties is isPurchased. It is a Boolean type.
I don't have a lot of experience with converters so I'm finding this a little difficult. I have 2 questions.
The 1st question is regarding syntax. I copied this example from here.
public class purchasedConverter : IValueConverter
{
public object Convert(inAppPurchases value, Type targetType, object parameter, string language)
{
return;
}
}
If the isPurchased == true then I'd like to set the background color to my stackpanel to a different color.
I changed object value to inAppPurchases value on the Convert method. However, no matter what I tried I could not get a reference to a Background.
I think I want to return Background="somecolor"
My 2nd question (assuming I can do the 1st part), is I'm using StandardStyles.xaml which comes with the Microsoft WinRT projects So my converter would exist there.
<StackPanel Grid.Column="1" VerticalAlignment="Top"
Background="CornflowerBlue" Orientation="Vertical" Height="130"
Margin="0,0,5,0"/>
However, like I said I've tried this before but I wasn't able to figure out how to add the convert to my .xaml file. Where would I reference the converter? Is it on the StandardStyls.xaml or the main .xaml that I'm viewing?
Any help is appreciated.
Background property of StackPanel is type of Brush (Panel.Background msdn) , so we can return object of type SolidColorBrush from Convert method.
Your converter should look like this:
class PurchasedConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// isPurchased is bool so we can cast it to bool
if ((bool)value == true)
return new SolidColorBrush(Colors.Red);
else
return new SolidColorBrush(Colors.Orange);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Next you must create instance of this converter in XAML:
<Window.Resources>
<con:PurchasedConverter x:Key="pCon" />
</Window.Resources>
And now you can use this converter to binding Background property in StackPanel:
<StackPanel VerticalAlignment="Top" Orientation="Vertical" Height="130"
Background="{Binding isPurchased, Converter={StaticResource pCon}}"
Margin="0,0,5,0" >
</StackPanel>
I need to set the property of cotrol which is a dependent of another property of its parent.
I try to explain better my problem with an example. I want to create a toggle switch button that animates a "slider" element into it. The dimensions of the toggle switch is being defined when the usercontrol is inserted into the application window. I want the slider be sized half larger than the switch case. So if the control is large 100, the slider should be 50, or if large 250, the slider should be 125. Then I need a sort of call to a function or something similar:
<UserControl>
<Border Name="switchCase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Border Name="slider" Width="**Container.Width/2**" ></Border>
</Border>
</UserControl>
Is there any possibilities to achieve this ??
Thanks in advance
Paolo
Yes you need databinding with a converter, such as the following example
xmlns:conv="clr-namespace:MyConverters.Converters"
.......
<UserControl.Resources>
<conv:WidthConvertercs x:Key="widthConv"></conv:WidthConvertercs>
</UserControl.Resources>
<Border Name="switchCase" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<Border Name="slider" Width="{Binding ElementName=switchCase, Path=ActualWidth, Converter={StaticResource widthConv}}" Background="DarkMagenta"></Border>
</Border>
Your converter class would be
[ValueConversion(typeof(double), typeof(double))]
class WidthConvertercs : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double withPar = (double)value;
return withPar/2.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I hope this helps
Out of the box it's not supported by XAML. You can only bind to Properties.
You can write a converter which do the calculation (or you can use
MathConverter)
You can do the calculation in the code behind in event
handlers
If you are following the MVVM pattern you can do the
calculation in the ViewModel (altough it will introduce view
related concepts to the ViewModels which is not always good...)
You can write your own Binding extension
Is it better to build 2 different pages? Such that when a button is clicked, it leads to another page with the table? Or can we hide the table until the "expand" button is clicked? Is it better to present the table as a listbox? Thanks.
I think a converter is the way to go, like TerrenceJackson said!
In the DataGrid your Binding should look like this:
<CheckBox Content="Expand" Height="72" Name=MyExpandCheckbox IsChecked="False" />
<DataGrid Visibility="{Binding ElementName=MyExpandCheckbox, Path=IsChecked, Converter={StaticResource BoolToVisibleConverter}}"/>
You have to set the Name of your Button/Checkbox/... for the ElementName in the binding for the visibility-property.
Here is an example of a BoolToVisibilityConverter:
public class BoolToVisibilityConverter : IValueConverter
{
#region IValueConverter member
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (Boolean)value ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
i would'nt do it with two seperate pages.
You can use a simple expander and style it for your purpose. Here's a video how to use the expander: http://silverlight.net/learn/videos/all/silverlight-toolkit-creating-a-toolkit-expander/ and here's a simple tutorial http://www.c-sharpcorner.com/Resources/768/.
You could also use a simple toggle button and bind the visibility of the table (DataGrid or ListBox, whatever you want) with a converter to the checked state of the toggle button. Somehow like this:
<ToggleButton x:Name="myToggleButton" />
<DataGrid Visibility="{Binding ElementName, Path=IsChecked, Converter={StaticResource BoolToVisibleConverter}}"/>
The BoolToVisibleConverter is a class which implements IValueConverter (http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx).
I don't know your exact aim, but I guess the expander approach is good for you.
Hope this helps.
BR,
TJ