The documentation for {X:bind} mentions that casting in a property path is possible (the mention {x:Bind obj.(TextBox.Text)}) as example. I can't see how this should work in the following simple example. I tried various combinations of the type name in parens, but no success.
A common use case would be a two-way binding for a combobox, here I would cast the SelectedValue property from the generic object type to a more specifc type.
Example xaml page MainPage.xaml:
<Page
x:Class="XBindTest4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XBindTest4"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" >
<StackPanel>
<ComboBox ItemsSource="{x:Bind SampleList, Mode=OneWay}"
SelectedValue="{x:Bind SampleData, Mode=TwoWay}"/>
</StackPanel>
</Page>
Code behind MainPage.xaml.cs:
using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;
namespace XBindTest4 {
public class SampleClass {
}
public sealed partial class MainPage : Page {
public ObservableCollection<SampleClass> SampleList = new ObservableCollection<SampleClass>(new[] {
new SampleClass(),
new SampleClass()
});
public SampleClass SampleData { get; set; }
public MainPage() {
this.InitializeComponent();
}
}
}
Currently, this is not possible with the existing syntax possiblities. That's bad because it adds unnecessary and repeating code to the binding expression: to use {x:Bind} in such a scenario, a converter has to be specified, even if it does nothing.
...
SelectedValue="{x:Bind SampleData, Mode=TwoWay, Converter={StaticResource NoOpConverter}}
...
If a converter is used, the apropriate cast is inserted into the generated file MainPage.g.cs:
this.dataRoot.SampleData = (global::XBindTest4.SampleClass)this.LookupConverter("NoOpConverter").ConvertBack((this.obj2).SelectedValue, typeof(global::XBindTest4.SampleClass), null, null);
Additional sources:
NoOpConverter.cs:
using System;
using Windows.UI.Xaml.Data;
namespace XBindTest4 {
public class NoOpConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, string language)
=> value;
public object ConvertBack(object value, Type targetType, object parameter, string language)
=> value;
}
}
Register the converter in App.xaml:
<Application
x:Class="XBindTest4.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XBindTest4"
RequestedTheme="Light">
<Application.Resources>
<ResourceDictionary>
<local:NoOpConverter x:Key="NoOpConverter"/>
</ResourceDictionary>
</Application.Resources>
</Application>
MainPage.xaml.cs:
<Page
x:Class="XBindTest4.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:XBindTest4"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" >
<StackPanel>
<ComboBox ItemsSource="{x:Bind SampleList, Mode=OneWay}"
SelectedValue="{x:Bind SampleData, Mode=TwoWay, Converter={StaticResource NoOpConverter}}"/>
</StackPanel>
</Page>
Related
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
}
I have been a few hours now trying to understand how to do data-binding.
Initially I was following some examples but they all show to do the databinding using {Binding Source={StaticResource myObject}, Path=myObject.myProperty}
or {Binding Path=myObject.myProperty}
Nothing of this seem to bind the Config object inside the controller that is inside the Window.
If I do the binding as an StaticResource it does the binding to an object of the Controller class but is NOT the object that is created inside the window class, this Config seems to be a new separate instance. This is the part I don't understand. If someone could explain or give me some reference where to look I would greatly appreciate it.
This is some code very simplified
Window1.cs
<Window x:Class="Sample.UI.Main"
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:controller="clr-namespace:Sample.Controller"
mc:Ignorable="d"
Title="SampleApp" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<controller:PublisherController x:Key="oController" />
</ResourceDictionary>
</Window.Resources>
<CheckBox x:Name="chkBoxShowRoom" Style="{StaticResource checkBoxTemplate}" Content="{StaticResource configShowRoom}" IsChecked="{Binding Source={StaticResource oController}, Path=Config.ShowRoom}"/>
Then my Window1.cs
public partial class Main : Window
{
public PublisherController Controller { get; set; }
Then Controller.cs
public class PublisherController
{
public Configuration Config { get; set; }
Then the Configuration.cs
public class Configuration : AbstractEntity, INotifyPropertyChanged
{
private bool _ShowRoom;
public bool ShowRoom
{
get
{
return _ShowRoom;
}
set
{
if (value != _ShowRoom)
{
this._ShowRoom = value;
OnPropertyChanged();
}
}
}
...
I have a DataGrid. I want to bind it to a List, which is in another class. Can I say the following?
<DataGrid ItemsSource="{Binding AnotherClass.Instance.MyList}">
...
</DataGrid>
I think this should work :
<Grid>
<DataGrid x:Name="MyDatagrid" ItemsSource="{Binding Path=MyList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" CanUserAddRows="False">
</Grid>
And set the itemsSource programmaticaly :
MyDatagrig.ItemsSource = MyClass.MyList;
I suggest using an MVVM approach.
Use an MVVM framework (like Prism, MvvMLight) or create by yourself a class for all viewmodels registration:
Locator.cs
public class Locator
{
public AnotherClass Another
{
get
{
return AnotherClass.Instance;
}
}
}
Add Locator.cs as an available resources for your view, so you can call your property setting the right DataContext:
MainWindow.xaml
<Window x:Class="DataGridBindingExample.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:DataGridBindingExample"
mc:Ignorable="d"
xmlns:vm="clr-namespace:DataGridBindingExample"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:Locator x:Key="Locator" />
</Window.Resources>
<DataGrid DataContext="{Binding Another, Source={StaticResource Locator}}" ItemsSource="{Binding MyList}">
</DataGrid>
</Window>
AnotherClass.cs
public class AnotherClass
{
private static AnotherClass instance;
private AnotherClass() { }
public static AnotherClass Instance
{
get
{
if (instance == null)
{
instance = new AnotherClass();
}
return instance;
}
}
public IList<string> MyList { get; set; } = new List<string>
{
"one",
"three"
};
}
I have a test WPF Window with the below XAML:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:wpfApplication2="clr-namespace:WpfApplication2"
xmlns:test="clr-namespace:WpfApplication2.Properties"
DataContext="{Binding Path=TestClass}"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<wpfApplication2:TestTypeConverter x:Key="TestConverter"/>
</Window.Resources>
<Grid>
<Grid Visibility="{Binding TestProperty, Converter={StaticResource TestConverter}, ConverterParameter='nottest'}">
<Label Content="Test Label"></Label>
</Grid>
</Grid>
</Window>
I have a test type-converter class which is below:
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfApplication2
{
class TestTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var valueToTest = (string) value;
var parameterToCheck = (string) parameter;
return valueToTest == parameterToCheck ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
}
TestClass is below:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using WpfApplication2.Annotations;
namespace WpfApplication2
{
public class TestClass : INotifyPropertyChanged
{
public TestClass()
{
TestProperty = "test";
}
private string _testProperty;
public string TestProperty
{
get { return _testProperty; }
set
{
if (_testProperty == value)
{
return;
}
_testProperty = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The visibility property isn't being affected by the type-converter, and the Convert method itself isn't even being called (I have placed a breakpoint which is not being hit).
What am I doing wrong?
Thanks
Your binding seems wrong:
Visibility="{Binding 'test', Converter={StaticResource TestConverter}, ConverterParameter='nottest'}"
change it to:
Visibility="{Binding test, Converter={StaticResource TestConverter}, ConverterParameter='nottest'}"
If the property test does not exist, no converter will be called
Create a resource for the converter parameter (as shown in the snippet below) and use the resource as converter parameter instead of the literal
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication2="clr-namespace:WpfApplication2"
xmlns:test="clr-namespace:WpfApplication2.Properties"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
DataContext="{Binding Path=TestClass}"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<wpfApplication2:TestTypeConverter x:Key="TestConverter"/>
<sys:String x:Key="converterParam">nottest</sys:String>
</Window.Resources>
<Grid>
<Grid Visibility="{Binding TestProperty, Converter={StaticResource TestConverter}, ConverterParameter={StaticResource converterParam}">
<Label Content="Test Label"></Label>
</Grid>
</Grid>
</Window>
Just omit the name of property altogether if you do not have a datacontext and it will work, The following wroked for me.
Visibility="{Binding Converter={StaticResource myTestConverter}}"
// see how Converter keyword is followed by Binding keyword and I did not specify property name after Binding keyword.
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>