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
Related
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
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.
I am trying to bind a List to a Combobox as datasource. My list is composed of custom class objects.
Binding works fine, but I can not set DisplayMember.
My class definitions; I have a custom class "Sett_Colection" that keep a List of another custom class
"Sett".
public class Sett
{
public string nameOfSett;
public Sett(){
...
}
}
public class Sett_Colection
{
public List<Sett> listOfSetts;
public Sett_Colection(){
...
}
}
The code in my Form is something like this;
public partial class Form1: Form
{
Sett_Colection collectionOfSetts;
public Form1()
{
// Here I add Sett instances into collectionOfSetts
// So collectionOfSetts.listOfSetts is not empty
combobox1.DataSource = collectionOfSetts.listOfSetts;
cmb_ayar.DisplayMember = "nameOfSett";
}
}
When I did this, datasource assigned succesfully. But display member has not been set as "nameOfSett".
The item names display as "Namespace.Sett";
I found a lot of example codes on internet, but none of them worked. I think my situation is a bit different
You need it to be a property:
public string nameOfSett {get; set;}
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.
I've been trying to find a nice neat and succinct way to declare RelayCommands in my ViewModels.
The best I can come up with is:
public class MyViewModel
{
public ICommand StopCommand { get; private set; }
public MyViewModel()
{
StopCommand = new RelayCommand(OnStop);
}
private OnStop(object sender)
{
//hammertime
}
}
What I'd really like to do it remove the two stage declaration/construction, something like:
public class MyViewModel
{
public readonly ICommand StopCommand = new RelayCommand(OnStop);
private OnStop(object sender)
{
//hammertime
}
}
However, this fails to compile with
error CS0236: A field initializer cannot reference the non-static
field, method, or property 'MyViewModel.OnStop(object)'
It there a neater / "standard" way that people use?
I've used the first format you specified quite a bit and it works fine for me.
Also - if you're using WPF, binding doesn't work with fields anyway so even if you can get the second approach to compile, it won't hook up to your UI.
One option is to abandon commanding which has it's limitations, and use another mechanism such as Actions provided by Caliburn.Micro. Then, you just need your view model verb:
public void Save()
{
}
<Button x:Name="Save">Save</Button>
I was using something like:
public ICommand StopCommand
{
get{return new RelayCommand(OnStop);}
}