Access static resource property from xaml? - xaml

I'm trying to access a static resource's property:
<ResourceDictionary>
<FeatureControl
x:Key="FeatureControl"
IsSweetFeatureEnabled="True">
<SweetFeature IsEnabled="{StaticResource FeatureControl.IsSweetFeatureEnabled}"/>
</ResourceDictionary>
But this gives me a runtime error.
All the posts I've found are dealing with wpf and not uwp.
I know I can pass FeatureControl and access the property from within the SweetFeature class, but the SweetFeature class does not need know about what other features are enabled.
Any ideas?
Edit
This is how the property is defined:
public class SweetFeature
{
public bool IsEnabled { private get; set; }
...
}

This should work for the binding:
IsEnabled="{Binding IsSweetFeatureEnabled, Source={StaticResource FeatureControl}}"

Related

System.Type as property of converter - only works with unused property in code behind

I have a IValueConverter that has a System.Type property which is set in XAML.
Converter:
internal class EnumTypeConverter : IValueConverter
{
public Type TypeToDisplay { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{
return TypeToDisplay?.FullName;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
XAML:
<Page
x:Class="UWPSystemTypeConverterTest.MainPage"
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:converter="using:UWPSystemTypeConverterTest.Converter"
xmlns:enums="using:UWPSystemTypeConverterTest.Enum"
mc:Ignorable="d">
<Page.Resources>
<converter:EnumTypeConverter x:Key="Converter" TypeToDisplay="enums:CustomEnum" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="{Binding Converter={StaticResource Converter}}" />
</Grid>
</Page>
When I run the application, I get following error:
Windows.UI.Xaml.Markup.XamlParseException: 'The text associated with
this error code could not be found.
Failed to create a
'UWPSystemTypeConverterTest.Converter.EnumTypeConverter' from the text
'enums:CustomEnum'. [Line: 14 Position: 56]'
If I add a property of type CustomEnum to the code- behind file, which is never used, the application works.
the changed code- behind- File:
public sealed partial class MainPage : Page
{
public CustomEnum WithThisPropertyTheAppWorks { get; set; }
public MainPage()
{
InitializeComponent();
this.DataContext = this;
}
}
The complete project for reproduction is here: https://github.com/SabotageAndi/UWPSystemTypeConverterTest
Line to uncomment is https://github.com/SabotageAndi/UWPSystemTypeConverterTest/blob/master/UWPSystemTypeConverterTest/MainPage.xaml.cs#L13
I suspect that an optimiser of UWP is causing this problem.
Is this really the case?
How can I fix the error without the unused property in the code-behind file?
Targeting UWP Build 10240, a viable work around is to add a dummy instance of the targeted enum in static resources of the page before instantiating the converter.
<Page.Resources>
<enums:CustomEnum x:Key="WorkAround">CustomEnumValue</enums:CustomEnum>
<converter:EnumTypeConverter x:Key="Converter" TypeToDisplay="enums:CustomEnum" />
</Page.Resources>
Info from a MSFT employee on a MVP mailing list:
This behaviour is a current limitation of UWP.
The XAML compiler and the runtime don't support System.Type- typed properties. So the needed metadata is not generated and the runtime can not convert the string to the type.
But because of the public properties on the code-behind, the compiler generates the needed metadata now. I am not that happy with the work around, but it is better than other solutions (e.g. a string property with the fullname to the type).

Property can't be found on ViewModel in UWP app

An Order form in UWP using Template 10 adds products to an order. The error is
Invalid binding path 'OrderViewModel.FindProduct_TextChanged' : Property 'OrderViewModel' can't be found on type 'ProductViewModel'
The relevant xaml snippet is
<Page.DataContext>
<ViewModels:MainPageViewModel x:Name="OrderViewModel" />
</Page.DataContext>
<GridView ItemsSource="{x:Bind OrderViewModel.Products, Mode=TwoWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="ViewModels:ProductViewModel" >
<AutoSuggestBox
Name="ProductAutoSuggestBox"
TextMemberPath="{x:Bind ItemCode, Mode=TwoWay}"
TextChanged="{x:Bind OrderViewModel.FindProduct_TextChanged}">
</AutoSuggestBox>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The relevant snippet from the OrderViewModel and the ProductViewModel
namespace ViewModels
{
public class OrderViewModel : ViewModelBase
{
public ObservableCollection<Product> Products { get; set; } = new ObservableCollection<Product>();
public void FindProduct_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{ ... }
}
public class ProductViewModel : ViewModelBase
{
string _ItemCode = default(string);
public string ItemCode { get { return _ItemCode; } set { Set(ref _ItemCode, value); } }
public ProductViewModel()
{
}
}
}
How to I correctly reference FindProduct_TextChanged on the OrderViewModel from the DataTemplate for the GridView which references ProductViewModel?
Voted up to #tao's comment. #Vague, I think you may misunderstand what x:DataType is used for. You can refer to the "DataTemplate and x:DataType" part of Data binding in depth:
When using {x:Bind} in a data template, so that its bindings can be validated (and efficient code generated for them) at compile-time, the DataTemplate needs to declare the type of its data object using x:DataType.
For your scenario, from your code public ObservableCollection<Product> Products { get; set; } = new ObservableCollection<Product>();, the type of your DataTemplate's data object should be your Product class, not your ProductViewModel, and in the meanwhile, your FindProduct_TextChanged event must be find in this Product class, that means your code of FindProduct_TextChanged should be placed in your Product data model.
By the way, I think there is no need to use TwoWay binding for ItemsSource. For this scenario, the binding target is ItemsSource of GridView, the binding source is ObservableCollection<Product> Products, I understand you want to update GridView when your collection is updated, this is work is done with ObservableCollection. Besides, only the binding source here can be changed to notify the binding target, so OneWay binding is enough. But it's not a big problem with your code.
So for your GridView, it should be something like this:
<GridView ItemsSource="{x:Bind OrderViewModel.Products, Mode=OneWay}">
<GridView.ItemTemplate>
<DataTemplate x:DataType="Models:Product" >
<AutoSuggestBox
Name="ProductAutoSuggestBox"
TextMemberPath="{x:Bind ItemCode, Mode=TwoWay}"
TextChanged="{x:Bind FindProduct_TextChanged}">
</AutoSuggestBox>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
if error is kind of like this I approved its a charset support bug:
Error Invalid binding path 'XX.YY' : Property 'ZZ' can't be found on type 'CCC'
Either xaml and C# supports unicode;
its because you use a non-ascii character in class properties. this is a bug I found today. Just rename your class proprty characters to ascii standards. Hope it will be fixed.

Page with type parameter

I would like to use new feature of UWP -> x:Bind. In order to that, all my pages need to have ViewModel property (as described in tutorials).
To avoid code duplicity, I have established base class as follows:
public abstract class BasePage<TBaseVM> : Page, where TBaseVM : BaseVM
{
public TBaseVM VM { get; private set; }
protected BasePage()
{
DataContextChanged += (s, e) => VM = e.NewValue as TBaseVM;
}
}
As you can see this BasePage class contains property called "VM" and property is of type BaseVM. Hence, I don't need to define VM property on each derived class.
Then I created derived page 'MainPage' defined in xaml as follows:
<pages:BasePage
x:Class="Realarm.View.Pages.MainPage"
x:TypeArguments="viewModel:MainVM">
By doing that, even Resharper's Intellisense offers me properties from "MainVM" in MainPage.xaml, thus is can write:
<ListView ItemsSource="{x:Bind VM.AlarmsVM}">
Unfortunately, when I try to build the project, I get error in MainPage.g.i.cs:
Severity Code Description Project File Line
Error CS0305 Using the generic type 'BasePage' requires 1 type arguments Realarm D:...\Realarm\obj\x86\Debug\View\Pages\MainPage.g.i.cs 13
Any help?
I got this working using Xamarin.Forms.
Base Page:
public abstract class BaseContentPage<TViewModel> : ContentPage where TViewModel : BaseViewModel, new()
HomePage.cs:
public partial class HomePage : BaseContentPage<HomeViewModel>
HomePage.xaml:
<d:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="clr-namespace:Sample.Pages;assembly=Sample"
xmlns:vm="clr-namespace:Sample.ViewModels;assembly=Sample"
x:Class="Sample.Pages.HomePage"
x:TypeArguments="vm:HomeViewModel">
<ContentPage.Content>
</ContentPage.Content>
</d:BaseContentPage>
Just add a x:TypeArguments definition at the top of the XAML:
<v:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:v="clr-namespace:YourApp.Views"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:YourApp.ViewModels"
mc:Ignorable="d"
x:TypeArguments="vm:HomeViewModel"
x:Class="YourApp.MainPage">
Worked for me as well when I set the BindingContext as given below in Base Page's constructor:
public BasePage()
{
BindingContext = new TBaseVM();
}

TwoWay Binding of a ComboBox to a static property in .NET 4.5

Well, i just posted this question, but i figured that i am not doing anything wrong. My code (and the code of all of the answers) is correct, but my dev-maschine runs with .NET4.5 which apparently has a problem with the binding of the combobox...
So here a new version of the question: How to two-way bind a combobox's SelectedItem to a static Property in .NET4.5?
The following code snippets work in .net4 but not in .NET4.5. In 4.5 it is just that the selected value is not propagated back into my static property.
My ViewModel:
public class MainWindowViewModel
{
public static List<String> MyElements { get; set; }
public static string SelectedElement { get; set; }
static MainWindowViewModel()
{
MyElements = new List<string>() {"a", "b", "c"};
SelectedElement = "a";
}
}
And my XAML
<Window.Resources>
<me:MainWindowViewModel x:Key="model"/>
</Window.Resources>
<StackPanel>
<ComboBox
ItemsSource="{Binding Source={x:Static me:MainWindowViewModel.MyElements}, Mode=OneWay}"
SelectedItem="{Binding Source={StaticResource model}, Path=SelectedElement}" />
</StackPanel>
Does anybody have an idea how to achieve this two-way binding of a ComboBox's SelectedItem to a static Property in .NET4.5?
Please refer to my report on Microsoft Connect. Every Selector control is infected by this issue.
Finally, a few weeks ago Microsoft released an appropriate patch which has already been distributed through Windows Update. see Knowledge Base KB2805222 (WPF - Issue 7)
WPF - Issue 7:
Assume that you change a selector property (such as, the SelectedItem property) or ComboBox.Text property by using a binding path that contains a static property. In this situation, the binding does not react to the changes. Specifically, the new value is not written to the data item.

Silverlight: Data Binding: Using of Dependency Properties within a Row of a Collection

I have an collection + its structure:
public class FunctionListBindStructure : AttributeBase
{
public FunctionListBindStructure() : base(true) { }
//this represents one row of the collection
public MyFunction Function { get; set; }
public string Name { get; set; }
}
public class FunctionListBind : AttributeListBase
{
//this represents
public ObservableCollection<FunctionListBindStructure> FunctionList { get; set; }
public FunctionListBind()
: base(true)
{
FunctionList = new ObservableCollection<FunctionListBindStructure>();
}
public override IList GetList()
{
return FunctionList as IList;
}
}
This class makes usage of a framework, which generates a Dependency Property for the CLR property Function.DisplayName as "FunctionDisplayNameProperty".
In my example view I bind this collection to a ListBox
ListBox ItemsSource="{Binding MyModel.FunctionListBind.FunctionList}" Height="52" HorizontalAlignment="Left" Margin="136,157,0,0" Name="listBox1" VerticalAlignment="Top" Width="130" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FunctionDisplayNameProperty, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The problem is now that only the last item of the collection is displayed in the list...the previous items are rendered only with space; although I am pretty sure (via debugger) that the dependendy properties (when they registered and their value set) of the previous rows shall have non-initial values. If I refer directly to the corresponding CLR property (Function.DisplayName) all works fine.
My question: Do I make here a design error? Are Dependency Properties not supposed to be used as a row type? I use the same pattern for non-collection and there it works. This is also the reason why I want to use the same approach for collections (I can use 90% of the exisitng codeline to generate and set the Dependeny Properties).
Thanks for any hints also how (if not a design error) to debug the Dependency Property binding.
It was just a bug of my framework coding. I have defined the dependency properties as an instance attribute, instead of a static one. Now all works fine.