The Granite State Hacker

Microsoft.Graph and Appointment Management

It’s been a minute since I’ve felt like there was some content to share of a coding variety, and ironically, I don’t have a minute to do it. So without further ado, I’ll dig right in.

In the management of Granite State Code Camp, I devised a way to connect with all the folks who had ever been a part of one of the events I’ve hosted in Teams. I sent a calendar invite as a 1:1 meeting initiation from myself to each of all the guests in the tenant, using Graph.

This goes back to 2020, when Granite State Code Camp rode out the pandemic as an online-only event, hosted entirely in Teams. Having hosted both GSCC and the Manchester Community College’s New Hampshire CyberSecurity Symposium in the same tenant every year since, there’s a good number of guests in the tenant that all represent a decent targeted mailing list for such events.

In the meantime, while Microsoft has done a fantastic job of touting CoPilot for Visual Studio, I found that Copilot had no understanding of the latest Graph libraries. How ironic that the immense greatness of Copilot stumbles on the immense greatness of new library updates, but that’s something to explore in another post.

While the invitation automation code is messy, and not clean enough for publishing, I was then in a situation where I had a bunch of calendar invitations, and it was really hard to figure out who had accepted, who had declined, and who had ignored.

So, I wrote a console utility to find and filter all the invitations, so that I could then follow up appropriately. This code will eventually become the basis of a re-write for the invitation itself, because I like the updates to the Graph API (which I didn’t use in the part that sends the invitations). Anyway, here’s the GitHub hosted Gist including the project file that shows the package/versions I used to arrive here.

Retrospectives, Tech in the 603, The Granite State Hacker

Context is for Kings

A retrospective in software development methodology is a look back at a project cycle. Usually, a retrospective refers to a formal meeting held at the end of a development cycle. Retrospectives provide context for where you are, and can help you figure out where course adjustments might be needed.

I’m going to start a series of project retrospective blog posts. My intent is to go a bit beyond the standard “lessons learned” aspect of a classic case study by discussing ways to address those lessons learned if I were delivering on the same requirements today.

I understand there’s some risk in this. The very nature of talking about “what I’d do different today” is a dynamic target. The answer could be different tomorrow, when you’re reading this. Perhaps that’ll be another chance to revisit the topic.

In these posts, I’m going to go over my own project history and do a full post-mortem on whole project deliveries. In order to protect potential client property, I’ll generally avoid identifying the client I did the work for and/or any publicly known project name. Teammates familiar with the projects will be able to identify them, but that’s ok.

Whenever working on a longer-term, non-trivial application, it’s inevitable that before the project is completed, technical debt sets in. Most commonly, some of the components are obsoleted by newer versions of tools used in the project, and there’s no immediate need or budget to update it. As long as those tools are still supported, it’s generally de-prioritized as a problem for another day. Longer term, everything is eventually obsoleted.

Another inevitable part of code development is the classic “if I had it to do over again” aspect. This is a big-picture extension on the old “Make it work, make it right, make it fast” practice. The first time through a project, there’s always “make it work” moves that end up being what gets delivered, without opportunity to get to the “make it right” / “make it fast” cycles. If I had the project to do over again, I’d “make it more right” or “make it faster” by doing ‘X’.

Likewise, every project cycle has its ups and downs in terms of team interaction. Some times folks are on the ball, and the code flows. Other times, there’s what we call blockers… things that hold up progress.

A blocker could be any number of things.

A common blocker is missing or incomplete requirements. It’s hard for a programmer to teach a computer to do work if the programmer doesn’t know how to do that work.

Another common blocker is access or permissions. A programmer might have a requirement to develop code that depends on another service. If that’s the case, the programmer might be able to build an abstraction of that service, but eventually will need permission to access that service in some form to do integration testing.

In this case, I’ll still take the classic retrospective approach. I’ll address the following questions:

  • What did we set out to do?
  • How and why did we deviate from plan?
    • What worked?
    • What didn’t work?
  • What would we like to do next time?
    • How could we improve what worked?
    • How will we minimize the risks of what didn’t work?
    • How will we address any technical debt incurred?

Some retrospectives avoid management in order to avoid addressing these questions politically. In better situations, some representative of management and/or project stakeholders are included in order to get a more complete view of the project.

In my posts on the topic, it’ll just be my perspective, and I’ll be addressing the questions holistically. Having completed the projects and disbanded the delivery teams, with minor name changing to protect the innocent, politics need no longer apply.

I’ll admit, there’s some self-gratification in doing these post-mortems publicly. I’ll be able to show off experience, and hopefully grab the attention of folks who’d like to produce similar projects. My hope is to inspire more of the latter and spark up the conversation around how to apply similar solutions (preferably with the “desired state” improvements).

Tech in the 603, The Granite State Hacker

Microsoft Most Valuable Professional (MVP)

Jim Wilcox – 2019-2020 Microsoft MVP – Developer Technologies

This showed up in the mail today! Despite the April 1st date, it’s not an April Fools’ gag after all! I’ve only ever seen one of these trophies in person before this one. I’ve been trying to stay chill about it…. but heck, here it is…

I’m profoundly honored and thankful to say that Microsoft has chosen to award me with this 2019-2020 “Most Valuable Professional” (MVP) award, in the category of Developer Technologies!

If you’re not familiar with this award program, check out the program’s official web site: https://mvp.microsoft.com

Tech in the 603, The Granite State Hacker

Unloading the UI Thread in C# on Windows 10 + UWP

First I want to thank Matthew Groves for hosting the 1st known C# Advent (English).  I was honored to be able to grab the spot for Friday, December 22, 2017, which, happily, is the start of my Christmas holiday week, as well.

The crux of this post is that most visible performance issues in a Windows application come from the presentation layer.  Specifically, anything that puts load or wait states on the main “UI Thread” will make the app/application appear to hang or become unresponsive for periods of time. This post talks about strategies for getting load off the UI as much as possible, beyond the async/await mechanism in C#.  Most such load can be unloaded to a worker thread fairly easily.  Other tasks can be awaited together. In some cases, a UI component is involved, and it becomes necessary to manage load that, for that reason, reason MUST stay on the UI thread.

I remember when I was a kid hearing of projects for stock traders that handled hundreds of data update events every second and being totally intimidated by the thought of it.  I knew I’d “come of age” in technology when, in 2017, I worked with a focused team (known as “Blockheads”) to build such an app.  This latest generation “stock blotter” ran stable, without memory leakage, and with no apparent lag at tens of Gigabytes per second! These general ideas stem back to the project I worked on in 2016-2017 with BlueMetal for Fidelity Investments’ Equity Trading team, called Artis OMT.  Artis OMT has been on Fidelity’s Equity Trading floor for over a year now, and will soon reach a year of full deployment.  While Artis OMT was WPF, this post looks at similar performance ideas in a similar but different platform:  Windows 10 UWP (store apps).

Artis OMT didn’t start out able to handle 90Gigabytes of incoming data.  We had to use JetBrains tools to identify code that was bogging down or hanging the main UI thread.   That analysis, alone, is perhaps the subject of a different post, or more, some day.

When folks start thinking about UI Thread execution, the first thing most think of is Dispatcher.BeginInvoke().  This method is how you add workload to the UI thread.  I’m trying to talk about how to UNLOAD the UI thread, and/or manage your load so that the user won’t observe UI freezes or lockups.

Here, however, are a few relatively easy ways to really make use of the extra cores in your CPU, and make your apps appear to perform much better:


Task.Run(() => { … });

Classic depiction of processes running in sequence vs in parallel


The title of this says it all, really.  Push a workload off the current thread.  Use whenever you have long running processes that you don’t have to touch UI controls from.   If you have timing dependencies, you can manage them with Task.When, Task,Wait, or even better, Task.ContinueWith().  Examples below cover this a little more.



Batch remote service calls using Tasks and WhenAll()

Service calls are low hanging fruit.  So often I see code that makes calls in series, waiting on the results of one before making the next call, even though the two calls have no dependencies on each other…  it’s just so much easier to write the sequence case that folks let it hang.   await Task.WhenAll(…) is not as syntactically sweet, but still MUCH sweeter than having to set up an aggregate event.

///

/// Does one request at a time, holding up the entire process
/// at each step until it completes. Simpler code but….
/// Total time spent is the sum of all tasks’ time.
///
public async void GetContentinSequence(Session session)
{
    var dbContent = awaitGetDatabaseContent(session);
    var webContent = await GetWebContent(session);
    var userProfile = await GetUserProfile(session);
    var userContext = await GetUserContext(session);
}


///

/// Executes all requests simultaneously, letting the default task dispatcher do its thing.
/// total time spent is no more than the longest running individual task, all other things being equal.
///
public async void GetContentinParallel(Session session)
{
    var contextTask = GetDatabaseContent(session);
    var webContentTask = GetWebContent(session);
    var userProfileTask = GetUserProfile(session);
    var userContextTask = GetUserProfile(session);
    var stuff = new Task[] { contextTask, webContentTask, userProfileTask, userContextTask };
    await Task.WhenAll(stuff);
    var dbContent = contextTask.Result;
    var webContent = webContentTask.Result;
    var userProfile = userProfileTask.Result;
    var userContext = userContextTask.Result;
}

Here’s an example that makes this more clear:

var start = DateTimeOffset.Now;

var task1 = Task.Run(async () => { awaitTask.Delay(1000); });
var task2 = Task.Run(async () => { awaitTask.Delay(1500); }); //1.5 seconds
var task3 = Task.Run(async () => { await Task.Delay(1000); });
var task4 = Task.Run(async () => { awaitTask.Delay(1000); });
var tasks = new Task[] { task1, task2, task3, task4 };
Task.WhenAll(tasks).ContinueWith(t => { Debug.WriteLine(DateTimeOffset.Now – start); });



outputs something like:
00:00:01.5623681

As always, there’s some overhead with task switching.  You’ll notice that the time was just a few ticks longer than 1.5 seconds.

What if you can’t unload the UI thread?  what if your long running process must interact with controls like a huge grid that needs to calculate an aggregation of a data set that lives in it?   
Here’s an option…


DoEvents() erhhh… ummm…  await Task.Delay(…)

I once scrubbed references to Visual Basic from my CV and landed a job that had scrubbed VB from the job description.  I didn’t want to work for a company that would hire a “VB-Weenie” and they didn’t want to hire a “VB-Weenie”, either… but there was VB6 work to do. 

One thing that VB6 had going for it was a concept called DoEvents().   It enabled you to give up processing the current method to allow any pending events to execute. It would then return to finish the calling method.

In C#, the closest equivalent, nowadays, is “await Task.Yield()” or await.Task.Delay(…).

Most folks talk about using “await Task.Yield()” at the start of an awaitable method to make sure the whole method runs asynchronously.  There’s some sense to that.   More importantly, one can interrupt long running processes that must run on the UI in order to allow the UI to respond to user inputs.  In testing, I’ve seen that Task.Yield() often doesn’t allow enough room for redraws of the UI.  Likewise, setting a Task.Delay of a 1 tick timespan isn’t enough, either.  1 millisecond delay, however, does seem to suffice in my basic testing.

private async void LongRunningAggregatorOnUIThread(object sender, object e)

{

    await Task.Yield();
    timer.Stop();
    var timeoutRate = TimeSpan.FromMilliseconds(100);

    
    var timeout = DateTimeOffset.Now.Add(timeoutRate);
    var value = 0L;
    while (true)
    {
        value++;
        if (DateTimeOffset.Now >= timeout)
        {
            textbox.Text = value.ToString();
            await Task.Delay(1);
            timeout = DateTimeOffset.Now.Add(timeoutRate);
        }
    };
}



As always, use this very carefully.  This has overhead of its own, as well, that can cause performance issues…. including potential deadlocks.

Tech in the 603, The Granite State Hacker

MVP IoT with Windows 10, C#, Raspberry Pi and Azure IoT Hub

Boston Code Camp 28 is in the books as one of the greatest community events of recent times in my book.  Attendance was fantastic, we had amazing sponsors, and a ton of great speakers, but the thing that really made things hop was attendance.  I’m personally convinced it was the best attended Code Camp I’ve ever participated in.
As a presenter, I don’t often get to attend as many of the amazing presentations as I’d like, but yesterday I found myself especially regretting that… there was just so much great content from so many great presenters. I did manage to get into more sessions than I usually do… how could I not?

Sure, you get a few “look at this cool stuff I can do” presentations.  These are great for folks looking for inspiration on the tough challenges.  I love the spirit of “look at this cool stuff you can do (if you apply your skills in a modestly different direction)”.
This is why I do this MVP IoT presentation.  This presentation is about taking skills you already know and love (namely C# development) and applying them to what’s classically though of as “embedded” development.  Yes, there’s overhead in this approach…  you might have to use hardware that’s slightly better equipped to handle Windows 10 IoT Core such as the Raspberry Pi 2b & 3b… but the difference is mute at small scale, and masked at large scale by not having to have a separate team with different skills. 

IoT apps on the Windows 10 IoT Core platform ARE Windows 10 apps.


The point I always try to drive home is IoT apps on the Windows 10 IoT Core platform ARE Windows 10 apps.  The very same exe you compile for your embedded device runs just as well on laptop, desktop, server, Windows 10 phone, et al.

I do regret the couple snags I had during my demos.  Still, someone approached me after the presentation with the idea that I should pre-record portions of my presentation in order to avoid these kinds of things.  I like to run live for several real reasons.  1)  I’m a coder, not a professional presenter… I don’t often have time to polish my presentations to that level.  2)  Glossing over rough edges hides what development is.  Development is tough.  You have to have backup plans and contingencies.  My Raspberry Pi didn’t want to connect to the guest WiFi, so I fell back to plan B, and moved on.  It’s a real world scenario;  you hit a snag, but keep pressing on.

IoT is already about the last mile of the Sci-fi story, bringing Internet omnipotence to the fringes of reality, enabling the “Computer” of Star Trek (or the Cortana of the modern desktop) to reach it’s potential (hopefully without becoming Skynet).

It’s the stuff of science fiction… but it’s not fiction.

Visual Studio 2017’s ability to debug into a remote embedded device and make hot changes to a running executable is beyond top notch; it’s the stuff of science fiction… but it’s not fiction.

[office src=”https://onedrive.live.com/embed?cid=90A564D76FC99F8F&resid=90A564D76FC99F8F%211283217&authkey=AKQG9LbQcNcUSkk&em=2&wdAr=1.7777777777777776″]

If you look through my post history, you’ll see how this presentation has evolved over the past decade.  It’s been an interesting evolution…  starting with connecting Windows Phone 7 to SharePoint.  Then Windows Phone 8 and SharePoint online (and the story got really muddy for a bit there).  Then CSOM hit and smoothed out the SharePoint side of things.  I changed the story to Field Enablement using Xamarin for iOS, Android, and Windows Phone with SharePoint as a back end for a bit. (It’s a surprisingly compelling story, even if it’s only academic).  Eventually I started focusing on UWP for Windows 10, and my love of small form factor development drew me back to Raspberry Pi. 
Finally, I’ved bridged off of SharePoint and started talking about Azure IoT Hub, which is the modern accepted best practice in the domain of this development stack.  This change happened so relatively quickly that if you notice, the synopsis for the session didn’t mention it.  (  https://www.bostoncodecamp.com/CC28/sessions/details/16540 )

One can explore the code I developed for the demo at git hub, here:
That leads me to my final bit:  I am sorry I ran out of time in my presentation.  I was so psyched to show how to send telemetry back to Azure IoT Hub, and while I got to step through the code that sends updated reports, I did not get to explore any of what that looks like in the Azure IoT Hub portal, and didn’t get to explore the event-driven API on the embedded side that allows you to send code with Device Twin down to a the device.
Here’s the event that commits the post:

        private async void iotHubButton_Click(object sender, RoutedEventArgs e)

{

try

{

using (var client = DeviceClient.CreateFromConnectionString(

$"HostName=BOSCC-IOTHub.azure-devices.net;DeviceId=GraniteStHacker;SharedAccessKey={Credentials.LuisAccessKeyFromAzurePortal}",

TransportType.Mqtt))

{

var twinProperties = new TwinCollection();

twinProperties["MeasuredTemperature"] = manager.MeasuredTemperature;

twinProperties["HeaterPowerOn"] = manager.HeaterPowerOn;

twinProperties["ACPowerOn"] = manager.ACPowerOn;

twinProperties["Device_BOSCC"] = DateTime.Now.ToString();

await client.UpdateReportedPropertiesAsync(twinProperties);

Console.WriteLine("Done");

}

}

catch(Exception ex)

{

Console.WriteLine(ex);

}

}

}


Click the image below to see the device I was using for the demo in Azure IoT Hub’s Device Twin view.

Here’s the resulting Device Twin JSON:
{
  “deviceId”: “GraniteStHacker”,
  “etag”: “AAAAAAAAAAE=”,
  “version”: 8,
  “status”: “enabled”,
  “statusUpdateTime”: “0001-01-01T00:00:00”,
  “connectionState”: “Disconnected”,
  “lastActivityTime”: “0001-01-01T00:00:00”,
  “cloudToDeviceMessageCount”: 0,
  “authenticationType”: “sas”,
  “x509Thumbprint”: {
    “primaryThumbprint”: null,
    “secondaryThumbprint”: null
  },
  “properties”: {
    “desired”: {
      “$metadata”: {
        “$lastUpdated”: “2017-11-14T01:48:45.8322179Z”
      },
      “$version”: 1
    },
    “reported”: {
      “MeasuredTemperature”: 74,
      “HeaterPowerOn”: true,
      “ACPowerOn”: false,
      “Device_BOSCC”: “11/18/2017 8:34:08 AM”,
      “$metadata”: {
        “$lastUpdated”: “2017-11-18T16:34:20.1258514Z”,
        “MeasuredTemperature”: {
          “$lastUpdated”: “2017-11-18T16:34:20.1258514Z”
        },
        “HeaterPowerOn”: {
          “$lastUpdated”: “2017-11-18T16:34:20.1258514Z”
        },
        “ACPowerOn”: {
          “$lastUpdated”: “2017-11-18T16:34:20.1258514Z”
        },
        “Device_BOSCC”: {
          “$lastUpdated”: “2017-11-18T16:34:20.1258514Z”
        }
      },
      “$version”: 7
    }
  }
}

Tech in the 603, The Granite State Hacker

UWP App Samples

I realized as I was presenting a guided tour of UWP App Samples that I’d come full circle on a set of presentations around my experience building Windows 10 UWP apps.   The overall process was kicked off in three decisions. 

1)  pick a framework (we settled on Prism + Unity),   

2) pick a control library (we went with the UWP Community Toolkit (which is since released 2.0 by the way, updated for 1710, Fall Creators Update… there may be another presentation in the works here)).

Finally,
3) we consulted the UWP App Samples.   ( https://github.com/Microsoft/Windows-universal-samples )

When it comes down to it, our goal was very ambitious, and we couldn’t have accomplished what we did without these three key pieces.   I’ve presented each of them over the past several months at the Granite State Windows Platform App Devs meetup. ( https://www.meetup.com/Granite-State-NH-WPDev/ )

At the time of the presentation, the categories in the Open Sourced UWP App Samples project were these:

  • App Settings,
  • Audio/Video/Camera,
  • Communications,
  • Contacts and Calendar,
  • Controls/Layout/Text,
  • Custom User Interactions,
  • Data,
  • Deep links and app-to-app Communication,
  • Devices and Sensors,
  • Files/Folders/Libraries,
  • Gaming, Globalization and Localization,
  • Graphics and Animation,
  • Holographic,
  • Identity/Security/Encryption,
  • Launching/background tasks,
  • Maps/Location, Navigation,
  • Networking/Web Services,
  • Platform Architecture,
  • Speech/Cortana,
  • Threading,
  • Tiles/Toasts/Notifications

If your app will do anything remotely associated with any of the above functionality, you need to consult the related sample projects before beginning.  They aren’t production-ready samples, but they are good samples, nonetheless.

These slides don’t do any justice at all to the UWP Sample Apps project, it was really just something to throw on the big screen while folks gathered.

[office src=”https://onedrive.live.com/embed?cid=90A564D76FC99F8F&resid=90A564D76FC99F8F%211281880&authkey=&em=2&wdAr=1.7777777777777776″]

Tech in the 603, The Granite State Hacker

Back To Where We Belong

Big changes for me lately, so more of a narrative personal post than a technical presentation…  and with apologies. I’ve been too wrapped up with the finer details of finishing up my project with Fidelity to get any blog posts out at all since November last year (yikes). 

I’ll get back to the regularly scheduled broadcast shortly.

About this time two years ago, I started a project with a financial firm out in Back Bay, in the John Hancock tower.  We rolled out SharePoint 2013 on-prem, and migrated it from multiple legacy farms… both of which were WSS3.0.   So between the build out and migration, I ended up on that project for a solid three months or so.  Not a bad gig, but… Boston commute (thank God for the Boston Express Bus), and no C# (not even ASP.NET).  It was really more of an IT Pro gig than development.  I was able to do some really fancy powershell stuff to manage the migration, but definitely not my first choice.

That fall, I landed a role on Fidelity’s new order management/trading desktop build-out. I have to say, that was roughly the kind of project I’ve been angling for, for years.  

Technology-wise, the only thing that could have made it any better was to be Windows 10/UWP based, maybe with mobile tendencies.  Alas, building rich clients of any sort is relatively rare, so .NET 4.6 is better than… not .NET at all. 

Still… All C#/WPF, desktop client…   

A good number of my most recent technical posts and presentations were heavily influenced by this project.   Sure, Boston again… for a good portion of it… but they let me work at Fidelity’s Merrimack location for a good chunk of the latter half of the project, too.

And it was maybe the longest running single version project I’ve been on in my life.  I was on it for 18 months…  a full three times longer than a “big” six month project.

All that to say I haven’t spent a full, regular day at a desk at BlueMetal Boston’s Watertown office in just about two full years. 

In that time, I’ve seen too many great teammates move on, and about as many new teammates join us.  We were employee owned back then, and I rode out the entire Insight purchase while “living” at the clients’ site.

Still, one thing that bothered me is a pattern I don’t intend to continue repeating.  When I took my position at Jornata that became BlueMetal, I accepted the title of Senior Developer, even though it appeared to be a step down from my Systems Architect title at Edgewater. 

My reason for accepting that was primarily that I was joining Jornata as a SharePoint make/break, and I needed to get a little more SharePoint experience under my belt before I was comfortable calling myself a SharePoint Solutions Architect.   By the time the BlueMetal merger worked itself around, I realized my options had opened broadly.  I got the SharePoint experience I needed, but it became very apparent that it wasn’t the experience I wanted.  Unfortunately, I was stuck with the “Senior Developer” title even on projects where my depth of experience went much deeper. 

SharePoint is cool for using, and even integrating, and IT Pros get a lot of mileage out of it, but as for software developers… well…  let’s just say I let SharePoint fuck up my career enough that I had to re-earn my “Architect” title.  (pardon my language, there’s no better word for it.)  I always deliver a win, but I’m a Visual Studio kinda guy.  I’m still happy to develop integrations with SharePoint, and support the SharePoint community… but while there’ve been major improvements in the past few months alone, Microsoft has been muddling it’s SharePoint developer story for years, and I let myself fall victim to it.

Thankfully, the Fidelity project did the trick…  it was just the level of high-touch, real Enterprise application development that I needed to earn my self respect back, and prove out my abilities in the context of BlueMetal. 

I’ll admit, while I feel this is restoring my title, it is certainly not lost on me that “Architect” at BlueMetal is a class (or two) above “Architect” in any of my previous companies.  I always felt I was there, even if I felt discouraged and unsupported by my former teams.  I am truly honored to be among those who’ve earned this title in this company, and very appreciative of the recognition.

At BlueMetal, I’m supported and inspired by my team, and really seeing this as validation that my career vector is now fully recalibrated.

I’ve said this before: meteorologists are very well educated with lots of fancy tools to help them be more accurate, but reality is that unless you’re standing in it, you don’t really have much hope of getting it truly right.  I have no intention of becoming a weatherman architect.  Hands-on the code is where my strength (and value) is, so that’s where I’ll always shoot to be.

Tech in the 603, The Granite State Hacker

Intro to IoT with C#, Windows 10 on Raspberry PI 3a, a la #BOSCC26

Despite the time crunch, I had expected to be able to dig in on more of the example code I’d prepared (heck, wouldn’t have spent so much time pulling that together if I’d thought I wasn’t going to get to it.)

I’ll also admit, my slides were more of a discussion guide than details, but there are a few good url’s in there to get you going with a PI or other IoT starter kit.

[office src=”https://onedrive.live.com/embed?cid=90A564D76FC99F8F&resid=90A564D76FC99F8F%211258505&authkey=AH4O2F_Cu16yiaE&em=2″ width=”402″ height=”327″]

GIT repository for the demo code
https://github.com/GraniteStateHacker/BOSCC-GPIO

Feel free to reach out to me with questions, I’m happy to help!

Tech in the 603, The Granite State Hacker

Need to Synchronize Columns of a Master/Detail Grid in DevEx WPF?

I generally prefer Telerik controls, but I’ve got a client that uses Developer Express.  I recently had a need to synchronize columns in a Developer Express WPF Master/Detail grid.   It’s a bit of an unusual circumstance, where we have Master / Detail records that use the same view interface, but found the TreeListControl unable to scale up to the demands of our use cases.  The client still wanted the detail grid’s columns to appear to functionally be the same column as the master record’s (with the ability to show/hide the detail).

Thankfully, the Dev Express GridColumn class is a DependencyObject, and all the needed properties are exposed as DependencyProperty’s.

Since the detail grid has the same data interface as the master grid, I was even able to clone the column definitions. 

Finally, since this was an MVVM project, I didn’t want the functionality in code-behind, so I abstracted the code for this into a Behavior.

The approach was to bind the Width, Visibility, and VisibleIndex properties of each master grid column to a cloned detail grid column, giving the two entities the appearance of being one functional entity.
Here’s the snippet representing the detail grid definition….



        <dxg:GridControl>

          <dxg:GridControl.DetailDescriptor>


                <dxg:DataControlDetailDescriptor ItemsSourceBinding="{Binding Details}">


                    <dxg:GridControl x:Name="DetailsGrid" AutoGenerateColumns="None" ColumnsSource="{StaticResource ColumnsCollection}" >


                        <dxg:GridControl.View    >




                            <dxg:TableView  AutoWidth="False"


                            AllowCascadeUpdate="False"


                            AllowFixedGroups="True"

                            ShowGroupPanel="False"

                            CellStyle="{StaticResourceDefaultCellStyle}"

                            NavigationStyle="Row"

                            ShowGroupedColumns="True"

                            AllowGrouping="True"

                            AllowEditing="False"

                            AllowScrollAnimation="False"

                            ShowFixedTotalSummary="False"

                            AllowHorizontalScrollingVirtualization="True"

                            HorizontalScrollbarVisibility="Auto"

                            RowStyle="{StaticResource RowStyle}"

                            AlternateRowBackground="{x:Static dxRes:DevExpressResources.AlternateRowBackgroundBrush}"

                            UseLightweightTemplates="None"

                            ShowColumnHeaders="False"

                               />

                        </dxg:GridControl.View>

                        <i:Interaction.Behaviors>


                            <local:SyncDetailGridColumnsBehavior/>


                        </i:Interaction.Behaviors>

                    </dxg:GridControl>

                </dxg:DataControlDetailDescriptor>

            </dxg:GridControl.DetailDescriptor>

        </dxg:GridControl>



Note the highlighted part above that introduces a local class called SyncDetailGridColumsBehavior, shown in its entirety below:

using System.Windows.Data;

using System.Windows.Interactivity;

using DevExpress.Xpf.Grid;


namespace Local
{
    public class SyncDetailGridColumnsBehavior : Behavior<GridControl>
    {
        private GridColumnCollection_parentGridColumns;
        private GridColumnCollection_detailsGridColumns;
        protected override void OnAttached()
        {
            _detailsGridColumns = AssociatedObject.Columns;
            _parentGridColumns = AssociatedObject.ResolveParentColumnCollection();
            InitializeMasterDetailGrid();
        }
       
        private void InitializeMasterDetailGrid()
        {
            _detailsGridColumns.CloneColumnsAndBindWidthsFrom(_parentGridColumns);
        }
    }
    internal static class ColumnHelpers
    {
        public static GridColumnCollectionResolveParentColumnCollection(this GridControl associatedObject)
        {
            var result =
                ((DevExpress.Xpf.Grid.GridControl)
                    ((System.Windows.FrameworkContentElement) associatedObject.Parent).Parent).Columns;
            return result;
        }
        public static voidCloneColumnsAndBindWidthsFrom(this GridColumnCollection targetGridColumns,
            GridColumnCollection sourceGrid)
        {
            targetGridColumns.Clear();
            foreach (var aSourceColumn in sourceGrid)
            {
                var aClonedColumn = aSourceColumn.Clone();
                aSourceColumn.BindWidths(aClonedColumn);
                aSourceColumn.BindPositions(aClonedColumn);
                targetGridColumns.Add(aClonedColumn);
            }
        }
        public static GridColumn Clone(this GridColumn source)
        {
            return new GridColumn()
            {
                Name = source.Name,
                Width = source.Width,
                Binding = source.Binding,
                Header = source.Header,
                Style = source.Style,
                CellStyle = source.CellStyle,
                CellTemplateSelector = source.CellTemplateSelector,
                CellTemplate = source.CellTemplate,
               
            };
        }
        public static void BindWidths(this GridColumn source, GridColumn bindingPartner)
        {
            source.SetBinding(BaseColumn.WidthProperty,
                new Binding(“ActualWidth”)
                {
                    Source = bindingPartner,
                    Mode = BindingMode.OneWay,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });
            bindingPartner.SetBinding(BaseColumn.WidthProperty,
                new Binding(“ActualWidth”)
                {
                    Source = source,
                    Mode = BindingMode.OneWay,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });
        }
        public static void BindPositions(this GridColumn source, GridColumn bindingPartner)
        {
            source.SetBinding(BaseColumn.VisibleIndexProperty,
                new Binding(“VisibleIndex”)
                {
                    Source = bindingPartner,
                    Mode = BindingMode.TwoWay,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });
           
        }
        public static void BindVisibility(this GridColumn source, GridColumn bindingPartner)
        {
            source.SetBinding(BaseColumn.VisibleProperty,
                new Binding(“Visible”)
                {
                    Source = bindingPartner,
                    Mode = BindingMode.TwoWay,
                    UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
                });
        }
    }
}
       

I expect this to cover 90% of our needs, the other 10% has to do with row selection across master/detail boundaries, but that’s a story for another day.

In the meantime, let me know how this makes ya feel…  leave a comment, below.

Thanks!

Tech in the 603, The Granite State Hacker

Intro to Rx.NET (Reactive Extensions)

Thanks to the gang for joining me at the Microsoft Store in Salem NH for my preso on “Intro to Rx.NET”   Being that it’s a toolkit I’ve been digging a lot at work lately, I had a feeling folks might appreciate a broad brush into to it.

Please check out the Granite State (NH) Windows Platform App Devs (#WPDevNH) on meetup.com to connect with the group and maybe even participate, yourself.  In addition to the core presentation topic, we had a great debate in speculation on how Microsoft’s purchase of Xamarin might settle out.  Also, I’ll be attending Build 2016, so we’re talking about having a special meeting early in April to recap and consider future presentations. (stay tuned!)

Rx reminds me a lot of other declarative language elements (XSL, XAML) in that it seems really natural, then you start looking at more advanced stuff and the complexity becomes boggling… then you start to really understand the abstractions and it feels natural again.

Without further ado, here’s my slides for the presentation:

[office src=”https://onedrive.live.com/embed?cid=90A564D76FC99F8F&resid=90A564D76FC99F8F%21824879&authkey=AC01r6jwJX5ng5M&em=2″ width=”402″ height=”327″]
I’d like to thank the folks at http://IntroToRx.com, I referenced them more than any other source putting this together.
Finally, for the code I demoed, please check out the post I mentioned, here:
Hope to see you soon!
-Jim Wilcox
The Granite State Hacker