I am currently building a WinRT app for Windows 10 and I am facing an issue I can't seem to find an answer for.
In my main page, I have a list of map markers bound to an ObservableCollection in my ViewModel. For each of these markers, i need to display a text that can either be Property1 or Property2 from my MapMarker class, based on the value of another property (let's call it PropertySelector) of my ViewModel.
The best solution I found is to make a struct that contains both Property1 and Property2 in the MapMarker class, bind it to the text field of the marker and use a Converter to choose which one to display.
Since you can't bind a property to a ConverterParameter, I implemented a DependencyProperty in the Converter to give it access to PropertySelector. The DP works fine, the property in the Converter gets updated, but the markers are never updated. I get that it's because I didn't trigger any event that actually told the marker to update, but I didn't manage to achieve it by adding a PropertyChanged("MarkerList") to the PropertySelector setter or by trying to refresh programmatically the bindings when I change the property with things like GetBinding(Text).UpdateSource(), that by the way seem to have a different implementation from WPF.
Am I doing this right ? What can I do to force the bindings to refresh ?
Here is my relevant code :
MainPage.xaml
<Page.Resources>
<local:PropertySelectorConverter x:Key="propertySelectorConverter"
PropertySelector="{Binding PropertySelector}" />
</Page.Resources>
...
<Maps:MapControl>
<Maps:MapItemsControl ItemsSource="{Binding MarkerList}">
<Maps:MapItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Properties, Converter={StaticResource propertySelectorConverter}}" />
</DataTemplate>
</Maps:MapItemsControl.ItemTemplate>
</Maps:MapItemsControl>
</Maps:MapControl>
<Button Text="Switch Data" Click="SwitchButton_Click" />
MainPage.xaml.cs
public void SwitchButton_Click(object sender, EventArgs e)
{
viewModel.PropertySelector= !viewModel.PropertySelector
}
ViewModel.cs
class ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Marker> markerList = new ObservableCollection<Marker>();
public ObservableCollection<Marker> MarkerList
{
get { return markerList; }
set { markerList = value; OnPropertyChanged("MarkerList"); }
}
private bool propertySelector = false;
public bool PropertySelector
{
get { return propertySelector; }
set { propertySelector = value; OnPropertyChanged("PropertySelector"); }
}
}
Marker.cs
public class Marker
{
public Tuple<double, double> Properties { get; set; } = Tuple.Create(10, 7);
}
Converter.cs
public class PropertySelectorConverter : DependencyObject, IValueConverter
{
public bool PropertySelector
{
get { return (bool)GetValue(PropertySelectorProperty); }
set { SetValue(PropertySelectorProperty, value); }
}
public static readonly DependencyProperty PropertySelectorProperty =
DependencyProperty.Register("PropertySelector", typeof(bool), typeof(PropertySelectorConverter), new PropertyMetadata(null, CurrentItemChangedCallback));
private static void CurrentItemChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
}
public object Convert(object value, Type targetType, object parameter, string language)
{
var properties = (Tuple<double, double>)value;
return PropertySelector ? properties.Item1 : properties.Item2;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Thank you for your time.
Lacking a good, minimal, complete code example that clearly illustrates your question, it is difficult if not impossible to provide specific advice. But there are some general thoughts to share…
First, in my experience the
ConverterParameter
is more useful for static (i.e. compile-time) information to be provided to the converter. E.g. when you have written a general-purpose converter that needs some specific data for a given binding, but where that data value is known at compile-time.In your scenario, you actually have multiple input values for the converter that vary at run-time. For this scenario, IMHO it is more appropriate to use
MultiBinding
. This allows you to provide two or more binding sources, where WPF will recomputed the bound value if any one of those sources change. Unfortunately, this is a WPF feature and, like many very useful WPF features, has been omitted from the Windows Store/Winrt API.However, you can construct a simple intermediary view model class to accomplish the same. For example:
Then use that in your XAML, something like:
Caveat: lacking a complete code example to start with, the above is just browser-written code. There may be syntax errors, or I might even have left out some key Windows Store-required code. For sure, the exact binding sources, paths, and XML namespace may need some tweaking, as I have no way to know for sure how you've set up your data contexts, etc.
But hopefully the above shows clearly enough the basic approach that you can use it in your project.
For completeness, here is what the WPF approach using
MultiBinding
would look like:A
MultiBinding
will always have a converter, implementingIMultiValueConverter
. TheConvert()
method of that interface looks a like that ofIValueConverter
, except that instead of theobject value
parameter allowing a single input value to be converted, it has theobject[] values
parameter.Based on the code you provided, I would expect your converter to look like this:
Then in your XAML, you would do something like this: