Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I re-engaged on this project over the last few weeks and finally came up with a solution. Given the comments and thoughts people posted above, I'm not sure anyone but me understands what I'm trying to do, but I figured it might be worthwhile posting how I solved this. At the minimum, writing it up will ensure <em>I</em> understand it :-).</p> <p>Summarizing the question again:</p> <p>I have a home control server that exposes objects in my home via a SOAP interface. <code>Home.LivingRoom.Fireplace</code>, for example is exposed as:</p> <p><code>http://server/Home/LivingRoom/Fireplace?property=DisplayName</code> <code>http://server/Home/LivingRoom/Fireplace?property=PowerState</code></p> <p>Doing an HTTP GET against these will result in the HTTP reply containing the property value (e.g. "Living Room Fireplace" and "Off" respectively).</p> <p>A garage door (e.g. <code>Home.Garage.EastGarageDoor</code>) is exposed as:</p> <p><code>http://server/Home/Upstairs/EastGarageDoor?property=DisplayName</code> <code>http://server/Home/Upstairs/EastGarageDoor?property=GarageDoorOpened</code> <code>http://server/Home/Upstairs/EastGarageDoor?property=Trigger</code></p> <p>Here we have a property that if set causes an action (<code>Trigger</code>). Doing a POST against this with a HTTP body of "True" will cause the door to open/close.</p> <p>I am building a WP7 app as a front end to this. I have decided to follow the Mvvm model and am using Mvvm-Light.</p> <p>WP7 does not have a built in way of supporting notifications from REST interfaces, and I am not yet ready to build my own (although it is on my radar). So for the UI to show up-to-date state I need to poll. The # of entities &amp; amount of data is relatively small and I have now proven that I can make it work well with polling, but there are some optimizations I can do to improve it (including adding smarts to the server to enable a notifcation like system).</p> <p>In my solution I've blurred the lines between my model &amp; my viewmodel. If you really wanted to be pendantic about it my "Model" is just the low level classes I have for wrapping my Http requests (e.g <code>GetPropertyAsync(objectLocation, propertyName, completionMethod)</code>).</p> <p>What I ended up doing is defining a generic class for properties. It looks like this:</p> <pre><code>namespace Premise.Model { //where T : string, bool, int, float public class PremiseProperty&lt;T&gt; { private T _Value; public PremiseProperty(String propertyName) { PropertyName = propertyName; UpdatedFromServer = false; } public T Value { get { return _Value; } set { _Value = value; } } public String PropertyName { get; set; } public bool UpdatedFromServer { get; set; } } } </code></pre> <p>I then created a <code>ViewModelBase</code> (from Mvvm-Light) derived base class <code>PremiseObject</code> which represents the base class each object in the control system is based on (e.g. which is literally called `Object').</p> <p>The most important method on <code>PremiseObject</code> is it's override of <code>RaisePropertyChanged</code>:</p> <pre><code> /// &lt;/summary&gt; protected override void RaisePropertyChanged&lt;T&gt;(string propertyName, T oldValue, T newValue, bool sendToServer) { if (sendToServer) SendPropertyChangeToServer(propertyName, newValue); // Check if we are on the UI thread or not if (App.Current.RootVisual == null || App.Current.RootVisual.CheckAccess()) { // broadcast == false because I don't know why it would ever be true base.RaisePropertyChanged(propertyName, oldValue, newValue, false); } else { // Invoke on the UI thread // Update bindings // broadcast == false because I don't know why it would ever be true GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(() =&gt; base.RaisePropertyChanged(propertyName, oldValue, newValue, false)); } } </code></pre> <p>Note a few things: 1) I am over-riding/re-purposing the <code>broadcast</code> parameter. If it is True then a the property change is "sent to the server" (I do an HTTP POST). I don't use broadcast property changes anywhere else (and I'm not actually even sure what I would use it for). 2) I always pass broadcast to False when calling <code>base.</code>.</p> <p><code>PremiseObject</code> has a set of standard <code>PremiseProperty</code> properties on it: Location (the URL to the object), Name, DisplayName, Value (the value property). DisplayName looks like this:</p> <pre><code> protected PremiseProperty&lt;String&gt; _DisplayName = new PremiseProperty&lt;String&gt;("DisplayName"); public string DisplayName { get { return _DisplayName.Value; } set { if (_DisplayName.Value == value) { return; } var oldValue = _DisplayName; _DisplayName.Value = value; // Update bindings and sendToServer change using GalaSoft.MvvmLight.Messenging RaisePropertyChanged(_DisplayName.PropertyName, oldValue, _DisplayName, _DisplayName.UpdatedFromServer); } } </code></pre> <p>So this means anytime <code>.DisplayName</code> changes within my program it gets relayed to all UI <em>and</em> IF AND ONLY IF <code>_DisplayName.UpdatedFromServer</code> is True does it also get sent back to the server.</p> <p>So how does <code>.UpdatedFromServer</code> get set? When we get our callback from an async Http request:</p> <pre><code> protected void DisplayName_Get(PremiseServer server) { String propertyName = _DisplayName.PropertyName; _DisplayName.UpdatedFromServer = false; server.GetPropertyAsync(Location, propertyName, (HttpResponseArgs) =&gt; { if (HttpResponseArgs.Succeeded) { //Debug.WriteLine("Received {0}: {1} = {2}", DisplayName, propertyName, HttpResponseArgs.Response); DispatcherHelper.CheckBeginInvokeOnUI(() =&gt; { DisplayName = (String)HttpResponseArgs.Response; // &lt;-- this is the whole cause of this confusing architecture _DisplayName.UpdatedFromServer = true; HasRealData = true; }); } }); } </code></pre> <p>Whenever the UI wants fresh data these XXX_Get functions get called (e.g. on a polling timer, when a view changes, app start, etc...)</p> <p>I have to duplicate the code above for each property I define which is pretty painful, but I have not yet found a way to genericize it (trust me, I've tried, but my knowledge of C# is just not strong enough and I just keep moving the problem). But this works, and works well.</p> <p>To cover all bases, here's an example of the Trigger property on the GarageDoor class:</p> <pre><code> protected PremiseProperty&lt;bool&gt; _Trigger = new PremiseProperty&lt;bool&gt;("Trigger"); public bool Trigger { set { if (value == true) RaisePropertyChanged(_Trigger.PropertyName, false, value, true); } } </code></pre> <p>Note how I force the <code>broadcast</code> parameter to <code>RaisePropertyChanged</code> to true and how this is a "Write only" property? This generates an HTTP POST against the 'GarageDoor.Location' URL + <code>?propertyName=</code> + <code>value.ToString()</code>.</p> <p>I'm pretty happy with this turned out. It's a bit of a hack, but I've now implemented several sophisticated views and it works well. The separation I've created will allow me to change the underling protocol (e.g. batching up requests and having the server only send changed data) and my ViewModels will not have to change.</p> <p>Thoughts, comments, suggestions? </p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload