Using VS 2013, C#, Windows Store App
I need to send one object from main Frame to new one and then work with it.
So i have main Frame, second Frame (for work with sended object) and DataModel.
Idea - is to display all data that i have at main frame, than choose one object, press on it, after pressing new Frame will appear and you can work with selected items in new frame.
Problem - how to send object from one Frame to another.
Currently i made next: create additional static class that with static property:
public static class GetCurrentEvent
{
public static Event CurrentEvent { get; set; }
}
So, at first i call to property of this class at main Frame, and save required object using it:
private void ItemView_ItemClick(object sender, ItemClickEventArgs e)
{
var clickedItems = (Event)e.ClickedItem;
GetCurrentEvent.CurrentEvent = new Event(
clickedItems.UniqueId,
clickedItems.Name,
clickedItems.Place,
clickedItems.Description,
clickedItems.Start,
clickedItems.End,
clickedItems.ImagePath
);
if (this.Frame != null)
{
this.Frame.Navigate(typeof(ChangeEvent));
}
}
After that i use this property in new Frame:
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
this.DataContext = GetCurrentEvent.CurrentEvent;
...
}
All works, but i think that it's not the perfect method.
So,the quesion how i can change code abowe for improving methods, or how i can send object from one class to another?
EDIT
Choosed varinat to send object from Frame to Frame - use Parameter:
var clickedItems = (Event)e.ClickedItem;
this.Frame.Navigate(typeof(ChangeEvent), clickedItems);
and then convert to required type in new Frame:
this.DataContext = (Event)e.NavigationParameter;
There are many many ways that this accomplished and this tends be an opinionated debate.
I typically opt for a simple solution, such as saving a state/session variable in a global accessible singleton. I call the singleton Global and keep it in the root of the namespace.
Example:
public sealed class Global
{
#region Singlton Contructor
Global() { }
static readonly Global instance = new Global();
public static Global Default
{
get { return instance; }
}
#endregion
#region Global Settings
public Settings Settings {get;set;}
private AuthenticatedUser _authenticatedUser;
public AuthenticatedUser AuthenticatedUser
{
get
{
return _authenticatedUser;
}
set { _authenticatedUser = value; }
}
private UserSession _currentSession;
public UserSession CurrentSession
{
get
{
if (_currentSession == null) _currentSession = UserSessionService.UserSessionFactoy();
return _currentSession;
}
private set { _currentSession = value; }
}
#endregion
}
CurrentSession in this case keeps track of the objects I want to pass frame to frame. And its easily accessed by using
Global.CurrentSession.SomePropertyOrObject
Related
I am actually trying to archive global variables in Xamarin where any page can consume it. After a lot of research, looks like the best way to archive such thing is using the Singleton design pattern. I am facing difficulty to implement this. take a look...
global.cs
using System;
namespace xamarin_forms
{
sealed class Global
{
public string test { get; set; }
private static Global _instance = null;
private Global()
{
}
static internal Global Instance()
{
if (_instance == null)
{
_instance = new Global();
}
return _instance;
}
}
}
App.xaml.cs
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class App : Application
{
Global global = Global.Instance();
public App()
{
InitializeComponent();
MainPage = new PageWelcome();
global.test = "123";
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Ok, so far, I just created my singleton class with a simple test property. I set this to 123 when I initialize my application.
Now, on another page, welcome page...I'd like to read the value that I set previously on the initialization...
PageWelcome.xaml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class PageWelcome : ContentPage
{
public PageWelcome()
{
InitializeComponent();
Global global = Global.Instance();
DisplayAlert("Alert", global.test, "OK");
}
}
}
Actually this is not working. It's returns me a null. So, how to use this correctly ? Thanks !
In your App's constructor, you first create an instance of PageWelcome. This instance reads the test property of your Global singleton and displays its contents in an alert. At this point, no value has been assigned to that property as far as I can see.
It is only after the PageWelcome constructor finishes that you actually assign a value to the test property of your singleton. Change your App constructor to
public App()
{
InitializeComponent();
global.test = "123";
MainPage = new PageWelcome();
}
and it should work as expected.
You don't need a Singleton.
Just create a static class with your variables static and you would be able to use them on any Page, like you want global variables.
// 1. Create static class Global with string _Test
public static class Global
{
public static void Init()
{
// your init class
}
private static string _Test { get; set; }
public static string Test
{
get => return _Test;
set => _Test = value;
}
}
// 2. Init Global in your App.cs
public App()
{
Global.Init();
}
// 3. Then use them on any page
public PageWelcome()
{
Global.Test = "123";
}
The Structure
I have a simple form that fires off a timer that checks for updates pretty regularly. The constructor of the form that starts on load looks like so:
public MainWindow()
{
InitializeComponent();
otherWindow = new TheOtherWindow();
if (Meta.hasUpdate)
{
updateImage.Source = new BitmapImage(new Uri("/MyProject;component/Images/updateTrue.gif", UriKind.Relative));
}
Thread updateMonitor = new Thread(() =>
{
UpdateManager updater = new UpdateManager();
updater.StartUpdateMonitor();
});
updateMonitor.IsBackground = true;
updateMonitor.Start();
}
The Meta class contains some very basic information, storing various strings that are referenced in several places but are sometimes updated. Among that structure is this:
class Meta
{
...
private static bool hasUpdate = false;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
}
}
The other piece is the UpdateManager class, which includes this a small routine to check for an update every 5 minutes.
class UpdateManager
{
Timer timer;
public void CheckForUpdates(Object source, ElapsedEventArgs e)
{
if (!isUpToDate())
{
timer.Stop();
Meta.SetHasUpdate(true);
Application.Current.Dispatcher.Invoke(new Action(() =>
{
MessageBox.Show("A new update is now available!);
}));
}
}
public void StartUpdateMonitor()
{
float updateInterval = 300000;
timer = new Timer(updateInterval); // Milliseconds between checks.
timer.Elapsed += CheckForUpdates;
timer.AutoReset = true;
timer.Enabled = true;
}
}
The Problem
In short, I want to fire off an event whenever Meta.SetHasUpdate() is reached that then broadcasts this to all the forms in the application with the goal of changing a small icon to indicate that an update is available.
My attempts to do so have ended with me learning that implementing INotifyPropertyChanged does not play nice with Static members. This was my attempt in implementing that...
class Meta : INotifyPropertyChanged
{
...
private static bool hasUpdate = true;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
NotifyPropertyChanged();
}
private static void NotifyPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged(null, new PropertyChangedEventArgs("hasUpdate"));
}
}
}
Since these members need to be read back from multiple forms, I can't make them not static without passing an object around a lot, which I don't want to do.
How do fire off an event that multiple forms can receive from the Meta class in this case? Do I need to consider a different structure, or am I misunderstanding INotifyPropertyChanged?
While there can be many ways to solve this, (think DI of your Meta class into each of your pages' ViewModels and react to INPC..that would be preferred over singleton approach), one approach to consider is using Messaging rather than Events. Messages, (offered in most MVVM frameworks), are great way to communicate between loosely coupled components. If you leverage an MVVM Library like MVVM Light, then this is very easy as it includes a Messenger implementation. The main advantage of this approach is that the forms that you want to receive the notification don't necessarily need to hold on to a reference of the source, like you would with an Event based approach.
Simply have all interested forms register for a message, and react accordingly when received.
For example, with MVVM Light, we can take advantage of automatically broadcasting a message when a INPC property has been updated.
private bool hasUpdate;
public bool HasUpdate
{
{
return hasUpdate;
}
set
{
// the last bool param indicates whether or not to broadcast a message to all interested parties.
Set(nameof(HasUpdate), ref hasUpdate, value, true);
}
}
Then in a totally separate / unrelated part of the app, (usually in a ViewModel), we can do this to indicate that we are interested in such an update:
Messenger.Default.Register<PropertyChangedMessage<bool>>(this, m => ReceiveHasUpdatedMessage(m));
and then in the receiving lambda:
private void ReceiveHasUpdatedMessage(PropertyChangedMessage<bool> m)
{
// react accordingly.
}
This is just one simple use case of the Messenger that MVVM Light provides.. you can do pretty much anything you want. The premise here is that using this approach decouples interested parties from requiring a hard reference to the emitter.
With a combination of everyone's very helpful advice, I've put together the following code. The MVVM solution is above, although I did not test it. If you aren't using MVVM though, this is what I did.
The UpdateManager class is the same. Meta has the following structure:
class Meta
{
private static bool hasUpdate = false;
public static event PropertyChangedEventHandler StaticPropertyChanged;
public static bool GetHasUpdate()
{
return hasUpdate;
}
public static void SetHasUpdate(bool value)
{
hasUpdate = value;
StaticNotifyPropertyChanged();
}
private static void StaticNotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}
}
Then, for any form I want to be aware of this kind of a change, I bolt in the following code:
public partial class SomeForm : Window
{
public SomeForm()
{
InitializeComponent();
Meta.StaticPropertyChanged += MethodThatTriggersOnUpdate;
...
}
private void MethodThatTriggersOnUpdate(object sender, EventArgs e)
{
myImage.Dispatcher.BeginInvoke(
(Action)(() => myImage.Source = new BitmapImage(
new Uri("/MyProject;component/Images/myNewImage.gif", UriKind.Relative))));
}
...
}
I have created a Class called "VNCVars.cs" and I want to be able to Save the data associated with all the Variables inside VNCVars to a XML file so I can load them back in on startup. I have another class called "SaveData.cs" which contains the code to generate the XML file.
I have code written that runs but my XML file is always empty.....can somebody point out what I have missed out??
public class VNCVars
{
//Global Variables for VNC 1 Location
//VNC File Location 1 Get and Set routines
private static string strVNC1Location;
public static string VNC1Location
{
get { return strVNC1Location; }
set { strVNC1Location = value; }
}
//Global Variables for VNC 2 Location
//VNC File Location 2 Get and Set routines
private static string strVNC2Location;
public static string VNC2Location
{
get { return strVNC2Location; }
set { strVNC2Location = value; }
}
//Global Variables for VNC 3 Location
//VNC File Location 3 Get and Set routines
private static string strVNC3Location;
public static string VNC3Location
{
get { return strVNC3Location; }
set { strVNC3Location = value; }
}
}
public class SaveXML
{
public static void SaveData()
{
var SaveData = new VNCVars();
XmlSerializer sr = new XmlSerializer(typeof(VNCVars));
TextWriter writer = new StreamWriter(#"c:\Fanuc\SetupVars.xml");
sr.Serialize(writer, SaveData);
writer.Close();
}
}
Then finally on my form I currently just have a button and on the click the following occurs...
private void button2_Click(object sender, EventArgs e)
{
SaveXML.SaveData();
}
Any help greatly appreciated....
You should use instance properties instead of static properties.
You can then use the singleton pattern to keep only one instance of this class.
If you need to have static variables and don't want to make this class a singleton, the way around is to wrap static variables:
[XmlElement("VNC1Location")]
public string VNC1LocationLocal
{
get
{
return VNC1Location;
}
set
{
VNC1Location = value;
}
}
I have a WPF application. In one of the XAML I have used Name attribute like as follows
x:Name="switchcontrol"
I have to access the control/property in .cs file using this.switchcontrol
My question is, I need to access the control in static method like
public static getControl()
{
var control = this.switchcontrol;//some thing like that
}
How to achieve this?
this is not accessible in static method. You can try save reference to your instance in static property, for example:
public class MyWindow : Window
{
public static MyWindow Instance { get; private set;}
public MyWindow()
{
InitializeComponent();
// save value
Instance = this;
}
public static getControl()
{
// use value
if (Instance != null)
var control = Instance.switchcontrol;
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Instance = null; // remove reference, so GC could collect it, but you need to be sure there is only one instance!!
}
}
Some alternatives to Tony's method - you could pass in the window (or whatever xaml construct you are using) as a reference to the method, e.g.
public static void GetControl(MainWindow window)
{
var Control = window.switchcontrol;
}
if you are going to be passing several different derived types of Window, you could also do this:
public static void GetControl(Window window)
{
dynamic SomeTypeOfWindow = window;
try
{
var Control = SomeTypeOfWindow.switchcontrol;
}
catch (RuntimeBinderException)
{
// Control Not Found
}
}
Class:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
public class MainService : IChat
{
IChatCallback ChatCallback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
Chat chat = new Chat(this);
public void ShowChat()
{
chat.Show();
}
public void SendInstantMessage(string user, string message)
{
chat.RaiseMsgEvents(user, message);
ChatCallback.InstantMessage(user, message);
}
}
Form:
public partial class Chat : Form
{
MainService service;
public Chat(MainService service)
{
InitializeComponent();
OnMsgReceivedEvent += new OnMsgReceived(callback_OnMsgReceivedEvent);
this.service = service;
}
private void btnSend_Click(object sender, EventArgs e)
{
service.SendInstantMessage("Admin", txtMsg.Text);
}
}
The mainForm use the class like this:
public partial class Form1 : Form
{
ServiceHost host;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
host = new ServiceHost(typeof(WCF_Server.MainService));
host.Open();
}
}
In the main form, i just pass the class, no initializing, but in the class when ShowChat() called i need to show the chat form and get to this class method so i can send messages.
.NET is an object oriented language. In fact, every class is an object.
The error you are getting is because you're instantiating an object with "this" on the global level.
UPDATE
Based on your update you could do the following and it will work. You might want to refactor this some more to ensure that it's not going to break any business rules etc.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
public class MainService : IChat
{
IChatCallback ChatCallback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
//Changed this to be just a declaration. This will be null,
// as there is no object yet, this is really just a pointer to nothing.
//This tells the system that you might/are planning to use an object called
//chat, but it doesn't exist yet.
Chat chat;
// Get your default constructor going. This will create the actual chat object, allowing the rest of your class to access it.
public MainService()
{
//instantiate it! (or as some of our co-ops say "We're newing it")
chat = new Chat(this);
}
//now that chat is actually instantiated/created you can use it.
public void ShowChat()
{
chat.Show();
}
public void SendInstantMessage(string user, string message)
{
chat.RaiseMsgEvents(user, message);
ChatCallback.InstantMessage(user, message);
}
}
This is just a personal pet peeve, but having a function parameter the same name as a global variable is... well for me a no no. I noticed this on your Chat.Chat(MainService) function.
Of course it is, just create a method that takes this class of yours as a parameter and call it...
As other posts have suggested, you'll want to re-consider how you instantiate your chat field within your example class. I would consider lazy loading the property, like so...
private ChatForm _Chat = null;
private ChatForm Chat
{
get
{
if (this._Chat == null)
{
this._Chat = new ChatForm(this);
}
return this._Chat;
}
set { this._Chat = value; }
}
Using lazy-loading will ensure you're able to use the keyword this upon request.