How to bind Entry to Xam.Plugins.Settings using BindingContext - c#

Binding seems somewhat more confusing in Xamarin Forms compared to C#. I installed Xam.Plugins.Settings. I simply want to bind bind an <Entry> to one of my settings. Here's what I have in my XAML:
<Grid BindingContext="{HelloWorld.Helpers.Settings}">
...
<Entry Placeholder="ESN"
Text="{Binding Path=Esn, Mode=TwoWay}"
...
/>
</Grid>
And here's my code behind:
namespace HelloWorld.Helpers
{
public static class Settings
{
private static ISettings AppSettings
{
get
{
return CrossSettings.Current;
}
}
#region Setting Constants
private const string EsnKey = "esn_key";
private static readonly string EsnDefault = string.Empty;
#endregion
public static string Esn
{
get
{
return AppSettings.GetValueOrDefault<string>(EsnKey, EsnDefault);
}
set
{
AppSettings.AddOrUpdateValue<string>(EsnKey, value);
}
}
}
}
But I'm getting the error: Position 7:15. Type HelloWorld.Helpers.Settings not found
This tutorial shows an example of how to set the BindingContext dynamically, but as you can see, the Settings class is static, and so I can't call new. I also want to be able to set the binding in the XAML. I then referred to this tutorial, so I tried to set the value of BindingContext in the XAML to {x:Static HelloWorld.Helpers.Settings}, but I get the same error.
All I want to do is map HelloWorld.Helpers.Settings.Esn to this <Entry>.
Any advice? I'm totally new to Xamarin, and I have very little C# experience.
Thanks in advance!
Update 1 5/26
#Alessandro solution works. I prefer not to have to write getters and setters again, since they're already written in Settings, but it works. I didn't have to use INPC though. Here is the code for my ContentPage in case anyone else has the same issue. Thanks.
namespace HelloWorld
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Configuration : ContentPage
{
public Configuration()
{
InitializeComponent();
BindingContext = new MySettings();
}
}
public class MySettings
{
public string Esn
{
get { return HelloWorld.Helpers.Settings.Esn; }
set { HelloWorld.Helpers.Settings.Esn = value; }
}
}
}
And then I removed the BindingContext property from within my XAML, since it's being set in the code behind.
Update 2 5/26
I just changed public static class Settings to public class Settings and public static string Esn to public string Esn. Then that prevents the need for MySettings. And instead I write BindingContext = new Settings();
I'm not sure why the plugin developer used static instead, but it was a simple solution it seems.

I think you should use a Class "MySettings" that use "Settings" and implement INotifyPropertyChanged (I use PropertyChanged.Fody for INPC)
[ImplementPropertyChanged]
public class MySettings
{
public string Url {
get { return Settings.Url; } // These are yours "Settings"
set { Settings.Url = value; }
}
public int Port {
get { return Settings.Port; }
set { Settings.Port = value; }
}
}

Remove static keywords from public static class Settings and public static string Esn
In the constructor for your ContentPage, add BindingContext = new Settings()
Remove the BindingContext property from the XAML
That's it.

Related

From MainWindow.xaml.cs class, why I am unable to access a custom property of App.xam.cs class

In MainWindow.xaml.cs class, when trying to get access to my property Name inside App.xaml.cs class, I'm getting the error:
'App' does not contain a definition for 'Name'.
Why?
Remark: I'm working a scenario similar to this one. But author there is using a global variable, and as we know, in most cases, using properties is preferred over global variable. So, I'm trying property in my case.
App.xaml.cs:
public partial class App : Application
{
private string[] name;
public string[] Name
{
get { return name; }
set { name = value; }
}
}
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
string[] myArray = App.Name //VS2019 Intellisense gives the error: 'App' does not contain a definition for 'Name'
}
}
You need to make the Name property static because it doesn't look like you are instantiating your App class inside your MainWindow class at all.
......
......
public partial class App : Application
{
private static string[] name;
public static string[] Name
{
get { return name; }
set { name = value; }
}
......
......
}
public string[] Name is an instance property of App, so you need to get a reference to instance before accesing data.
the running WPF instance can be obtained from static property Application.Current (requires cast)
var app = Application.Current as App;
string[] myArray = app.Name;

How to inherit ViewModel class to UserControl?

Suppose that I've a ViewModel class with different property such as:
//MainVm will inherit ViewModel that contains IPropertyChanged implementation
public class MainVM : ViewModel
{
publi bool Foo { get; set; }
}
I instatiated this class on the main controller in this way:
MainController mc;
public MaiWindow()
{
mc = new MainController();
DataContext = mc;
InitializeComponent();
}
the MainController have this implementation:
public class MainController : MainVM
{
some methods
}
so each time I need to access to a property of MainController on each UserControls I need to do: Application.Current.MainWindow.mc.Foo
and this is't elegant at all.
Is possible access to the property of specific ViewModel without call the code line above?
Thanks.
UPDATE
For add more details and clarification to this question: so in my UserControl I need to access to the Foo property that is part of MainVM. I'm spoke about the UserControl xaml code (not the controller of the UserControl), this is an example:
public partial class ControlName : UserControl
{
public ControlName()
{
InitializeComponent();
}
public btnAdd_Click(object sender, RoutedEventArgs e)
{
//Suppose that I need to access to the property Foo of MainVM here
//I need to access to MainWindow instance like this:
Application.Current.MainWindow.mc.Foo
//isn't possible instead access to the property directly inherit the class, like:
MainVm.Foo
}
}
In order to get the configuration App-Wide, you could use the
ConfigurationManager.AppSettings["Whatever"];
These settings are located in the App.Config in the following form
<appSettings>
<add key="Whatever" value="Hello" />
</apSettings>
But as I undertand, you have a ViewModel that let Users change the settings, in this case you should go for:
Properties.Settings.Default.myColor = Color.AliceBlue;
You ViewModel could expose this property as:
public Color MyColor
{
get {
return Properties.Settings.Default.myColor;
}
set {
Properties.Settings.Default.myColor = value; RaisePropertyChanged();
}
}
public void Persist()
{
Properties.Settings.Default.Save();
// Raise whatever needed !
}
From other ViewModels you can access these setting as well:
Properties.Settings.Default.myColor
Have a look here https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/using-application-settings-and-user-settings and here https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-create-application-settings

How to access App.Current properties in Windows Universal Apps

I am trying to bind to App.Current.XYZ properties in my View, however this doesn't seem to be possible, here's an example of what I have:
sealed partial class App : Application
{
public MyClassType MyClass { get; private set; }
...
And here is the View:
<Page ...
DataContext="{Binding MyClass, Source={x:Static Application.Current}}">
So, this isn't possible because x:Static is no longer supported in Windows Universal (or WinRT), so I have tried exposing the application property through a property in the code-behind, like this:
public MyClassType MyClass
{
get
{
return Application.Current.MyClass;
}
}
This doesn't work either! There is no intellisense for MyClass, it's completely missing. I have also tried App.Current and still no luck.
Any ideas why my property is not visible through Application.Current.? Or if there is any way I can bind to this property directly through XAML?
You need to cast Application.Current to your type like so:
public MyClassType MyClass
{
get
{
return ((App)Application.Current).MyClass;
}
}
Here is something that may work for you:
Create two classes:
public class MyDataProvider
{
private static readonly MyDataContainer _myDataContainer = new MyDataContainer();
public MyDataContainer MyDataContainer { get { return _myDataContainer; } }
}
public class MyDataContainer
{
public MyClassType MyClass { get; private set; }
...
}
Then in App.xaml define this static resource:
<resources:MyDataProvider x:Key="MyDataProvider"/>
Now you should be able to use data binding like this in your XAML code:
Attribute="{Binding MyDataContainer.MyClass, Source={StaticResource MyDataProvider}}"
In your case you could tweak the code so that MyDataContainer is actually your app:
public class MyDataProvider
{
public Application App { get { return Application.Current; } }
}
and write your data binding like this:
Attribute="{Binding App.MyClass, Source={StaticResource MyDataProvider}}"
In general however I would not use the App class as a provider for sources for data binding. For separation of concerns I would use something like I have above with MyDataProvider and MyDataContainer

When accessing window from view model it is always null

What i want to do is to create property into the model ComboBoxItemChange.cs of type ILoginView that is the interface which LoginWindow.xaml.cs is deriving. Using this property i want to grant access to the elements inside LoginWindow. I red that this is correct way to do it using MVVM pattern.
My problem is that property is always null.
LoginWindow.xaml.cs
public partial class LoginWindow : Window, ILoginView
{
public LoginWindow()
{
InitializeComponent();
this.DataContext = new ComboBoxItemChange();
(this.DataContext as ComboBoxItemChange).LoginWindow = this as ILoginView;
}
public void ChangeInputFieldsByRole(string role)
{
MessageBox.Show(role);
}
}
ComboBoxItemChange.cs
public class ComboBoxItemChange : INotifyPropertyChanged
{
public ILoginView LoginWindow { get; set; }
private void ChangeloginWindowInputFields(string value)
{
if (LoginWindow == null)
return;
LoginWindow.ChangeInputFieldsByRole(value);
}
}
ILoginView.cs
public interface ILoginView
{
void ChangeInputFieldsByRole(string role);
}
As stated in comment:
There are two different instances you are creating:
One in code behind where you set ILoginView to window itself
Second in Grid resources where you haven't set ILoginView.
Remove the instance you declared in XAML and let the bindings resolved from the instance you created in code behind. (DataContext will automatically be inherited for child controls).

Using MEF, how to export a view for an ItemsControl?

enter code hereMaybe the title is not so specific.
The situation which I'm having is. I've got an ItemsControl where I insert many ViewModels, and this ItemsControl should have to show the View through DataTemplates.
So, I write these in a ResourceDictionary:
And then, I add this ResourceDictionary to the ApplicationResources.
This is so redundant and tiredsome.
I'm using MVVM also, so I was thinking if could be a way to use MEF to discover the corresponding the View that should draw. I was investigating that creating a custom attribute tag could be a good idea to simplify these redundant code, maybe adding this tag in the view telling it that this ViewModel should draw for this View, but I get lost with MEF.
The plan is to remove the ResourceDictionary.
Can you lend me a little hand?
Thanks in advance.
In my host WPF application, I added this Import:
[ImportMany("ApplicationResources", typeof(ResourceDictionary))]
public IEnumerable<ResourceDictionary> Views { get; set; }
code behind for the ResourceDictionary:
[Export("ApplicationResources", typeof(ResourceDictionary))]
public partial class ItemView : ResourceDictionary
{
public ItemView()
{
InitializeComponent();
}
}
For reference, the Xaml for the example ResourceDictionary looks like this:
<DataTemplate DataType="{x:Type local:ItemViewModel}">
...
</DataTemplate>
in WPF application, before the main window:
// Add the imported resource dictionaries
// to the application resources
foreach (ResourceDictionary r in Views)
{
this.Resources.MergedDictionaries.Add(r);
}
[System.ComponentModel.Composition.InheritedExport(typeof(ProblemView))]
public abstract class ProblemView : UserControl // or whatever your Views inherit
{
public abstract Type ViewModelType { get; }
}
[System.ComponentModel.Composition.InheritedExport(typeof(ProblemViewModel))]
public abstract class ProblemViewModel : BaseViewModel // or whatever your ViewModels inherit
{
}
// in your App class
{
[ImportMany(typeof(ProblemView))]
public ProblemView[] Views { get; set; }
[ImportMany(typeof(ProblemViewModel))]
public ProblemViewModel[] ViewModels { get; set; }
void MarryViewViewModels()
{// called during MEF composition
foreach (ProblemView view in Views)
{
foreach(ProblemViewModel vm in ViewModels)
{
if(Equals(view.ViewModelType, vm.GetType())
{// match -> inject the ViewModel
view.DataContext = vm;
break;
}
}
}
}
}
// example of usage
public partial class SomeView : ProblemView
{
public override Type ViewModelType { get { return typeof(SomeViewModel); } }
}
Let me explain you how to setup something like this. You can look for further information in official documentation
Best implementation would be using interface and duck typing.
public interface IModule {
DataTemplate Template { get; set; }
string Name{get;set;}
...
}
Then for each plugin, inherit this interface
[Export(typeof(IModule ))]
public class SampleModule : IModule {
private DataTemplate template;
public DataTemplate IModule.Template {
get { return this.teplate; }
set { this.template = value; }
}
private string name = "SamplePlugin";
public string IModule.Name{
get { return this.name ; }
set { this.name = value; }
}
...
}
Class SampleModule is in separate assembly while IModule is in common with both Application and every module assembly.
Now you need to load every module available to application. This code snippet is from window of application
...
[ImportMany]
public IEnumerable<IModule> ModulesAvailable {get;set;}
...
public void LoadModules(string path) {
DirectoryCatalog catalog = new DirectoryCatalog(path);
catalog.ComposeParts(this);
}
Now you can just use foreach loop and add them to Application Resource
foreach(IModule module in ModulesAvailable) {
Application.Current.Resources.Add(module.Template, module.Name);
}
This is just concept and code is not tested.
I ssed MEF in my high-school final project I did few months ago, so you could take a look at my code. It is spreadsheet application with formula support where all operations and operands are loaded as plugins so it is very flexible.

Categories