A few people expressed an interest in understanding how AutoCAD 2015’s ability to open HTML pages inside its frame – shown in this recent post – could be extended to work with “native” user interfaces. This post shows how that can be done using WPF – the other supported mechanism for adding new documents to AutoCAD’s main frame introduced in the 2015 release.
Just as with last week’s post, there really isn’t that much to it: you just need to add your own WPF UserControl to the project and load that into an Autodesk.AutoCAD.Windows.WPFDocumentWindow object and pass it to other version of the DocumentCollection.AddWindowDocument() method. So I went ahead and added a little code to load some hardcoded XML data into a DataGrid hosted by said UserControl. It’s very basic – I haven’t taken care of any real-world concerns in this sample – but then it’s better than staring at a blank window. :-)
Here’s the updated C# code that adds our ADW command to the code we saw last week. You’ll notice it also adds a couple of single-line commands to tile child windows vertically and horizontally, to show how simple that is to implement.
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Windows;
using System;
using System.Net;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace NewDocumentWindow
{
// To check whether a page exists, we can use a HEAD request
// rather than a full GET. Derive a custom client to do that
class HeadClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
var req = base.GetWebRequest(address);
if (req.Method == "GET")
req.Method = "HEAD";
return req;
}
}
public class Commands
{
// Asynchronous helper that checks whether a URL exists
// (i.e. that the URL is valid and can be loaded)
private async static Task<bool> PageExists(string url)
{
// First check whether the URL is valid
Uri uriResult;
if (
!Uri.TryCreate(url, UriKind.Absolute, out uriResult) ||
uriResult.Scheme != Uri.UriSchemeHttp
)
return false;
// Then we try to peform a HEAD request on the page
// (a WebException will be fired if it doesn't exist)
try
{
using(var client = new HeadClient())
{
await client.DownloadStringTaskAsync(url);
}
return true;
}
catch (WebException)
{
return false;
}
}
[CommandMethod("BLOG")]
public async static void OpenBlog()
{
const string url =
"http://blogs.autodesk.com/through-the-interface";
// As we're calling an async function, we need to await
// (and mark the command itself as async)
if (await PageExists(url))
{
// Now that we've validated the URL, we can call the
// new API in AutoCAD 2015 to load our page
Application.DocumentWindowCollection.AddDocumentWindow(
"Kean's blog", new System.Uri(url)
);
}
else
{
// Print a helpful message if the URL wasn't loadable
var doc = Application.DocumentManager.MdiActiveDocument;
var ed = doc.Editor;
ed.WriteMessage(
"\nCould not load url: \"{0}\".", url
);
}
}
[CommandMethod("ADW")]
public static void AddDocumentWindow()
{
// Hardcode some XML data
var xdoc =
XElement.Parse(@"
<orders>
<order Customer=""ACME"" Item=""Widget"" Price=""100"" />
<order Customer=""ACME"" Item=""Tyre"" Price=""200"" />
<order Customer=""Amazon"" Item=""Pen"" Price=""50""/>
<order Customer=""Amazon"" Item=""Paper"" Price=""20""/>
<order Customer=""Autodesk"" Item=""CAD"" Price=""10""/>
<order Customer=""Autodesk"" Item=""File"" Price=""40""/>
<order Customer=""ABC"" Item=""Flowers"" Price=""503""/>
<order Customer=""ABC"" Item=""Sand"" Price=""706""/>
</orders>"
);
// Create our WindowContent WPF UserControl and set the
// XML data on our DataGrid object
var wc = new WindowContent();
wc.MyGrid.DataContext = xdoc;
// Create a WPFDocumentWindow for our content then open it
var dw = new WPFDocumentWindow(wc);
dw.Title = "WPF Document Window";
Application.DocumentWindowCollection.AddDocumentWindow(dw);
}
[CommandMethod("TV")]
public static void TileVertically()
{
Application.DocumentWindowCollection.TileVertically();
}
[CommandMethod("TH")]
public static void TileHorizontally()
{
Application.DocumentWindowCollection.TileHorizontally();
}
}
}
Here’s also the XAML definition of the WPF UserControl I added to the project. Again, very simple stuff.
<UserControl
x:Class="NewDocumentWindow.WindowContent"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc=
"http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<DataGrid
x:Name="MyGrid"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=Elements[order]}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
FontFamily="Gisha" FontSize="24">
<DataGrid.Columns>
<DataGridTextColumn
Header="Customer"
Binding="{Binding Path=Attribute[Customer].Value}"/>
<DataGridTextColumn
Header="Item"
Binding="{Binding Path=Attribute[Item].Value}"/>
<DataGridTextColumn
Header="Price"
Binding="{Binding Path=Attribute[Price].Value}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
When we run the ADW command, we see our new window gets created inside the AutoCAD frame:
If we run the TH command, we see our two windows get tiled horizontally:
Running the previously-defined BLOG command and then tiling vertically using the TV command gives us three windows across the width of the application frame:
With sub-windows in tiled mode, the Windows theme does impact the overall visual impression given by AutoCAD’s dark theme, but then this is likely to be an uncommon way for people to work (and then it’s always an option for the user to adjust the Windows theme, too).
That’s it for this post. From a UI perspective there are still some capabilities I’d like to explore in AutoCAD 2015, particularly around the theming of PaletteSets. Hopefully we’ll take a look at that in the next week or so.