How to bind TextBlock to self-made FrameworkElement property? - c#

I have a 'GameControl : FrameworkElement'. I have it in the xaml like this:
<local:GameControl x:Name="control"/>
This GameControl has a property that is an own class:
public Gem selectedGem {get; set;}
Now, I want to write this Gem's information into a TextBlock, so that the player will see its properties.
How do I bind my own FrameworkElement's properties to the MainWindow's elements?
--
Full xaml:
<Window x:Class="GemTowerDefense.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:GemTowerDefense"
mc:Ignorable="d"
Title="Gem Tower Defense" Height="670" Width="800"
ResizeMode="NoResize">
<Grid>
<Border Background="Gray" Height="600" Width="600" Margin="3,26,189,3">
<local:GameControl x:Name="control"/>
</Border>
<Border Background="LightSlateGray" HorizontalAlignment="Left" VerticalAlignment="Top" Height="285" Margin="608,181,0,0" Width="170">
<TextBlock x:Name="tbInfo" Text="Gem information">
</TextBlock>
</Border>
</Grid>
</Window>
(Instead of Text=Gem Information, I want the binding to the control's selectedGem, or to one of its string type property)

You make your property a dependency property and when binding that property to the TextBlock.Text, use a converter. Search Stackoverflow to find billion examples on both topics. The binding would look something like this:
Text="{Binding ElementName=control, Mode=OneWay, Path=selectedGem, Converter={local:ExampleConverter}}"
I find it easiest to create converter in code-behind:
public class ExampleConverter : MarkupExtension, IValueConverter
{
public ExampleConverter()
{
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if(value != null && value is Gem)
return (value as Gem).GemAsText();
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
#endregion
}

Related

Shorthand way of specifying strongly typed values in Xaml

Here's what I would like to end up with:
<Grid Visibility={Binding EnablePurchase, Converter={local:ConditionalConverter TrueValue=(Visibility)Visible FalseValue=(Visibility)Collapsed}}/>
Here's currently what I am doing:
<Grid>
<Grid.Visibility>
<Binding Path="EnablePurchase">
<Binding.Converter>
<local:ConditionalConverter>
<local:ConditionalConverter.TrueValue>
<Visibility>Visible</Visibility>
<local:ConditionalConverter.TrueValue>
<local:ConditionalConverter.FalseValue>
<Visibility>Collapsed</Visibility>
<local:ConditionalConverter.FalseValue>
</local:ConditionalConverter>
</Binding.Converter>
</Binding>
</Grid.Visibility>
</Grid>
You can simply create a converter which has properties like this:
public class ValueConverterWithProperties : IValueConverter
{
public int TrueValue { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and then use it like this:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<converter:ValueConverterWithProperties TrueValue="5" x:Key="converterWithProperties"></converter:ValueConverterWithProperties>
</Window.Resources>
<Grid>
<CheckBox IsChecked="{Binding item, Converter={StaticResource converterWithProperties}}"></CheckBox>
</Grid>
you can also derive from MarkupExtension in your converter for a much nicer usage:
public class ValueConverterWithProperties : MarkupExtension, IValueConverter
{
public int TrueValue { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((int) value == TrueValue)
{
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then you can set the property directly where you are using the converter allowing you to set different values per converter easily:
<Window x:Class="Converter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:converter="clr-namespace:Converter"
Title="MainWindow" Height="350" Width="525">
<Grid>
<CheckBox IsChecked="{Binding item, Converter={converter:ValueConverterWithProperties TrueValue=5}}"></CheckBox>
<CheckBox IsChecked="{Binding item2, Converter={converter:ValueConverterWithProperties TrueValue=10}}"></CheckBox>
</Grid>

Implementing IValueConverter to convert string to image

I am currently trying to displaying images in my Windows 8 application. I have a method which populates a property of type List<string> with a number of paths to images. I wish to display these images on screen.
Thus, I have implemented a converter to go from string to image. However, I get the errors :
The name "StringToImageConverter" does not exist in the namespace
"using:TestApp.Converters".
'TestApp.Converters.StringToImageConverter' does not implement
interface member
'Windows.UI.Xaml.Data.IValueConverter.ConvertBack(object,
System.Type, object, string)'
'TestApp.Converters.StringToImageConverter' does not implement
interface member
'Windows.UI.Xaml.Data.IValueConverter.Convert(object, System.Type,
object, string)'
Here is the code from my Converter :
namespace TestApp.Converters
{
public sealed class StringToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
try
{
return new BitmapImage(new Uri((string)value));
}
catch
{
return new BitmapImage();
}
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
And from my XAML file :
<common:LayoutAwarePage
...
xmlns:converters="using:TestApp.Converters"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Page.Resources>
<converters:StringToImageConverter x:Key="StringToImageConverter"> </converters:StringToImageConverter>
</Page.Resources>
...
<ItemsControl ItemsSource="{Binding Path=test}" Grid.Column="1" Grid.Row="3" Grid.ColumnSpan="4"
HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Converter={StaticResource StringToImageConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
Should this work for displaying my images in the Windows 8 application? The List<string> of image paths is called test and is in the code behind of the xaml file.
Thanks very much for any and all help with this :)
Apparently there are two types of IValueConverters:
Windows.UI.Xaml.Data.IValueConverter
System.Windows.Data.IValueConverter
It sounds like your framework is expecting the former, while you're implementing the latter.
You probably also need to change this:
xmlns:converters="using:TestApp.Converters"
to this:
xmlns:converters="clr-namespace:TestApp.Converters"
Windows.UI.Xaml.Data.IValueConverter expects the last parameter to be a string, not a CultureInfo
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:p="clr-namespace:System;assembly=mscorlib"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:Entities="clr-namespace:Entities;assembly=Entities"
mc:Ignorable="d"
x:Name="XXXXX"
x:Class="AAAA.XXXXX" Title="Seciones" Height="644.305" Width="909.579"
xmlns:c="clr-namespace:AAAA">
<Window.Resources>
<c:StringToImageConverter x:Key="stringToImageConverter"/>
</Window.Resources>
.....
</Window>

Binding a property to a style/resourcedictionary of a view

One of my views consists of 5 UserControls that each display data about a certain object. Let's say for example that the view displays the cows our company has, and on the screen cows 1 through 5 are displayed (each in their own UserControl).
What I want to do (but not sure is possible) is to bind the status of a cow to the style used in its respective UserControl. So we have a property status that could be ok, hungry, dead for example. In case the cow is ok I want to display a 'normal' style, if it's hungry I want the background to be red and if it's dead I want the text to be black and the fontsize increased.
I've added a simplified version of what I'm trying to achieve. My knowledge of WPF styles/resource dictionaries is still somewhat limited though.
What I basically want in code
A ViewModel with a Status property
class CowInfoViewModel : Screen
{
public string Name { get; set; }
public string Status { get; set; } //"ok", "hungry", "dead"
}
A View that retrieves a style or resourcedictionary
<UserControl x:Class="WpfModifyDifferentView.Views.CowInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- A reference to a ResourceDictionary with styles, that is bound to the 'Status' property -->
<StackPanel>
<TextBlock x:Name="Name" Text="Cow Name"/>
<TextBlock x:Name="Status" Text="Ok" />
</StackPanel>
</UserControl>
EDIT - Solution:
I did the following using Vale's answer:
In the xaml (reference to the converter):
<UserControl.Resources>
<Converters:CowStyleConverter x:Key="styleConverter" />
</UserControl.Resources>
In the xaml (elements):
<TextBlock x:Name="Name" Text="Cow Name" Style="{Binding Path=Style, ConverterParameter='TextBlockCowName', Converter={StaticResource styleConverter}}" />
The converter (note I left out the checks):
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var status = value.ToString();
var styleName = parameter.ToString();
_resourceDictionary.Source = new System.Uri(string.Format("pack://application:,,,/Resources/ScreenI2Style{0}.xaml", status));
return _resourceDictionary[styleName];
}
Then I created multiple ResourceDictionaries with styles such as:
<Style x:Key="TextBlockCowName" TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource SomeBrush}" />
</Style>
You can bind UserControl Style property to Status and use a converter.
<UserControl x:Class="WpfModifyDifferentView.Views.CowInfoView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfModifyDifferentView"
Style="{Binding Path=Status, Converter={StaticResource myConverter}}">
<UserControl.Resources>
<local:MyConverter x:Key="myConverter" />
</UserControl.Resources>
I assume that your converter is in WpfModifyDifferentView directly.
Converter will look like this:
public class MyConverter : IValueConverter {
private ResourceDictionary dictionary;
public MyConverter() {
if (dictionary == null) {
dictionary = new ResourceDictionary();
dictionary.Source = new Uri("pack://application:,,,/WpfModifyDifferentView;Component/Resources/Styles.xaml");
}
}
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
switch (value.ToString()) {
case "ok":
return dictionary["myKeyForOkStyle"] as Style;
case "hungry":
return dictionary["myKeyForHungryStyle"] as Style;
case "dead":
return dictionary["myKeyForDeadStyle"] as Style;
default:
return null;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
}
}
You need to specify the correct URI of course.

C# / WPF - DesignData - Binding to DesignData Collection Properties

I like design time data, especially when creating small widgets. For this very simple use case I'm having trouble binding to the properties of a design-time list which I have created in xaml.
Please find my ViewModel, View and SampleData below;
ViewModel
internal class SummaryViewModel : ViewModelBase
{
public string Title { get; set; }
public IList<Person> PersonList { get; set; }
internal SummaryViewModel()
{
PersonList = new List<Person>();
}
}
Sample Data
<ViewModel:SummaryViewModel xmlns:ViewModel="ViewModel" Title="Test Title">
<ViewModel:SummaryViewModel.Connections>
<ViewModel:ConnectionViewModel Id="0" />
<ViewModel:ConnectionViewModel Id="1" />
</ViewModel:SummaryViewModel.Connections>
</ViewModel:SummaryViewModel>
View
<StackPanel x:Class="View.SummaryView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100"
d:DesignWidth="100"
d:DataContext="{d:DesignData Source=/DesignData/SampleSummaryViewModel.xaml}"
Orientation="Vertical"
Background="LightGreen">
<!-- This Works -->
<Label FontSize="10" FontWeight="Bold" Content="{Binding Title}" />
<!-- This Works -->
<ListBox ItemsSource="{Binding PersonList}" />
<!-- This DOESN'T work -->
<Label FontSize="8" Content="{Binding PersonList, Path=Count}"/>
</StackPanel>
How would you configure SampleData such that you could bind to the Count of a list specified therein?
I have tried setting the resource type as both DesignData and DesignDataWithDesignTimeCreatableTypes with no luck.
It should be:
<Label FontSize="8" Content="{Binding Path=PersonList.Count}"/>
Also MÃ¥rten is correct, you should use an ObservableCollection instead.
HTH
CityView, just as a side note: to debug DataBinding I usually use an empty converter which only returns the value it was given. I put a breakpoint in there and that way I can see what exactly is going back and forth.
public class BindTestConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}
Combined with that and what the Output window tells me usually leads me to a solution to the problem at hand.
It should work, but become a one-time binding since your list does not implement INotifyPropertyChanged and therefore the binding is not updated when Count changes.
Try using an ObservableCollection<Person> instead.

How to get the selected index of MVVM-bound radiobuttons?

I have an ItemsControl which is bound and set to an observablecollection in my viewmodel:
<ItemsControl ItemsSource="{Binding AwaySelection}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding AwayText}" ></RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now, how to find out which one is clicked? I would like to bind the IsChecked value of each Radiobutton to a single variable in the viewmodel that returns an index to the collection. This would make it very easy for me to directly reference the selected item. Any ideas?
This is how I solved this problem. I wrote an EnumToBool converter for this, like
public class EnumToBoolConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value,
Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if (parameter.Equals(value))
return true;
else
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return parameter;
}
#endregion
}
And I've the following enumeration
public enum CompanyTypes
{
Type1Comp,
Type2Comp,
Type3Comp
}
Now, in my Xaml, I'm passing the types as the converter parameter.
<Window x:Class="WpfTestRadioButtons.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfTestRadioButtons"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:EnumToBoolConverter x:Key="EBConverter"/>
</Window.Resources>
<Grid>
<StackPanel>
<RadioButton IsChecked="{Binding Path=Type,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static local:CompanyTypes.Type1Comp}}" Content="Type1"/>
<RadioButton IsChecked="{Binding Path=Type,
Converter={StaticResource EBConverter},
ConverterParameter={x:Static local:CompanyTypes.Type2Comp}}" Content="Type2"/>
</StackPanel>
</Grid>
</Window>
Now, in your view model, you should have a property (in this case Type), which is of that Enum type.
Like,
public CompanyTypes Type
{
get
{
return _type;
}
set
{
_type = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Type"));
}
}
In this example, you might have noticed that Radiobuttons are static. In your case, as you are listing the radio buttons inside an Item control, you need to bind your ConverterParameter of your RadioButton as well, to the correct type.
In the end, I put the radio buttons into a listview, and bind the isselected property of the listview to the radiobutton one.
link Forum post describing this technique
When use MVVM with radiobutton control exits a problem on method onToggle(), but you can create a radiobutton for that.
public class DataBounRadioButton: RadioButton
{
protected override void OnChecked(System.Windows.RoutedEventArgs e) {
}
protected override void OnToggle()
{
this.IsChecked = true;
}
}
Then add reference to control and Binding a property, in my case IsActive.
<controls:DataBounRadioButton
IsChecked="{Binding IsActive}"/>

Categories

Resources