platform specific ui interop layers generated base mdl model definition generated viewmodel xaml...
TRANSCRIPT
Jason MorsePrincipal Engineering ManagerOffice Core Experience TeamPutting it all together, Office & XAML
XAML Case Study: Putting it All Together, Office and XAML
2-776
Andrew CoatesSenior Software EngineerOffice Core Experience Team
Office values and goalsApproach to universal applicationsWhere we are today
Agenda
How do you use XAML if you don’t care about easy?
There are many adjectives people use to describe Office…
Functional, powerful, productive, feature rich, complicated…
Fast and light are not usually among them.
First a bit of background on Office
All native, mostly C++• Hails from pre-C++ days over 30 years ago.• Continues to evolve with rapid adoption of C+
+11.
It has grown progressively in that time• Up to about 171 million lines at last count .• Not just Word, Excel, PowerPoint anymore.
Most versions on other platforms have been forks• These aren’t included in the 171 million lines.
The Office codebase
Originally we sold Office for machines.• Increasingly we are selling to
people.
What does this mean?• Available wherever you are.• Consistent experience.• New features need to roll out
everywhere.
The business has changed
Changing the adjectives• Fast & fluid• Still powerful
Sharing code• Leveraging what we have for new platforms.• Maximize code sharing between platforms.• Still use enough platform specific code to
guarantee the best experience possible on that platform.
We do care about development speed…• But complex and expensive solutions are fine as
long as we can centralize them.
So what do we care about?
Traditional databinding is too slow.• Set data into controls in code-behind.• x:Bind does a great job of addressing this, use it!
Don’t make every property a DP.• Custom DependencyProperties are significantly slower than
built-in ones.
Use only as many elements as you need.• Collapsed trees still have cost.• Either load optional elements dynamically or use
x:DeferLoadStrategy.• Previously VSM would create extraneous DependencyObjects.
Don’t be afraid of code-behind!
Using XAML at the speed of light
In the Windows 8.1 timeframe we looked at how we could squeeze every ounce of performance out of XAML.
So {Binding} is not super fast• When we started x:Bind did not exist• Push data into XAML using code behind• Code behind can interact directly with native C++ types
Wanted a binding replacement to help write repetitive code-behind
How we work with data in XAML
We call this XAML scriptingPreprocess the XAML file during a pre-build stepCustom XAML markup defines:• Properties.• “Scripts”, to push data into the XAML elements.• Define when each script should run.
Generate code-behind from the markupXAML team used same concept for x:Bind design
A solution based on custom XAML markup
<o:Property Type="Unicorn" Name="User"/>
<o:Script Trigger="User.Name"> tb->Text = User->Name;</o:Script>
<TextBlock x:Name="tb" />
Roughly the same as:<TextBlock Text="{x:Bind User.Name, Mode=OneWay}"/>
A quick glimpse at Office’s XAML scripting
The Good• Similar performance gains as we can now get from
x:Bind.• Switching yielded 50% faster boot for Calendar.
Unintended side effects• Meant to be just for pushing data.• Too easy to write logic in the UI.• Logic becomes platform specific.• Hard to unit test.
Scripting side-effects
Sometimes what happens and what you think will happen aren’t quite the same.
Business logic should be separate from UI code• Always been important.• Even more so in todays cross platform world.
Encapsulated in its own object• The UI can communicate directly with this object.• Object to UI communications should be through events.
This allows the object to be shared
Metapoint on how to build UI
• Model defined in a markup language.
• Generates a base class and platform specific interop layers.
• Developer fills in ViewModel implementation.
Moving to markup generated ViewModels
Model Definition
Generated ViewModel
XAML Interop(INPC)
XAML UI
JNI Interop
Java UI
iOS Interop
iOS UI
Unit Tests
MDL
Generated Base
Interop Layers
Platform Specific UI
Great for XAML to C++ interop• Makes writing code against XAML dramatically
easier.• Generates winmd interfaces for you.
Has some drawbacks to be aware of• Can mask complexity or cost.• Some limits on how C++ can be integrated.• Not cross platform.
Works great as a thin interop layer
C++CX is the glue for C++ apps
Overview of C++CX pros and cons.
Understanding C++CX classes
public ref class Foo sealed : public IBar{public: // public class properties/methods property int Number; void FooMethod();
// Ibar method override virtual void BarMethod() override;
internal:
void NativeMethod(CppModel* model);
private: CppModel* m_model;};
class Foo : public RuntimeClass<__IFooPublicNonVirtuals,
IBar>{public: // __IFooPublicNonVirtuals interface methods virtual HRESULT get_Number(int* num) override; virtual HRESULT put_Number(int num) override; virtual HRESULT FooMethod() override;
// IBar methods virtual HRESULT BarMethod() override;
void NativeMethod(SomeCPPStruct* model);
private: CppModel* m_model;};
C++CX C++ (using WRL)
// some simple code…control->RenderTransform->X = 25.0;
// turns intocontrol->QueryInterface(IID_PPV_ARGS(pUIE.GetAddressOf()));pUIE->get_RenderTransform(&pTransform);pTransform->QueryInterface( IID_PPV_ARGS(pTranslate.GetAddressOf()));pTranslate->put_X(25.0);
// what’s so bad? Well…If (control->RenderTransform->X != newTransformX) control->RenderTransform->X = newTransformX; If (control->RenderTransform->Y != newTransformY) control->RenderTransform->Y = newTransformY;
Understanding what C++CX generates
It’s so easy! What could possibly go wrong?
C++ types can be used natively in CX• The issue is how to get those objects in the first place.
In the same binary it’s easy• Can call internal functions directly.
Across binaries it is harder• Calls out work so you can use a locator pattern.• Object^ can be typecast to IInspectable* via reinterpret_cast.• Exported setter functions are a pattern we use a lot.
__dllexport void SetModel(C^ c, const Model& m){ c->SetModel(m);}
Using C++ types in ref classes
Main strategies for handling native types from within ref classes.
Let’s say you have a C++ class you want to expose to x:Bind
class UnicornImpl{public:void RegisterPropChanged(function<void(int)>&);wstring GetName();
};
Composing C++ classes
ref class UnicornCX : BaseINPC{ UnicornCX() : m_native(make_shared<UnicornImpl>) { m_native.RegisterPropChanged([=]( int propId) { if (propId == idName) NotifyPropertyChanged(“Name”);}); } property Name { String^ get { return ref new String(m_native.GetName().c_str()); }}shared_ptr<UnicornImpl> m_native;
}
Wrapping with CX
How about if you really want to use full inheritance in C++?
import “Windows.Foundation.idl”;[uuid(…)][exclusiveto(Unicorn)]interface IUnicorn : IInspectable{ [propget] HRESULT Name([out, retval] HSTRING*v);}
runtimeclass Unicorn{ interface IUnicorn; interface INotifyPropertyChanged;}
With C++ first generate a header and winmdFun with SDK tools:1. Make an IDL2. Run it
through midl /winrt to create a header and .winmd file.
3. mdmerge.exe allows merging with your own winmd files.
class UnicornWRL : RuntimeClass<IUnicorn, INPCImpl, UnicornImpl>{ UnicornWRL() { RegisterPropChanged([](int propId) { if (propId == idName) NotifyPropertyChanged(HStringReference(L“Name”).Get());}); } STDMETHODIMP get_Name(HSTRING* value){ return HStringReference(GetName().c_str()).CopyTo(value); }}}
Now use WRL to inherit from your base class
Use CustomResource loader• Can load resources from our existing resource
pipelines.• As an example, for colors:
Background=“{OfficeBrush PaneBackground}” becomes:
Background=“{CustomResource Brush.123}”
• Preprocessing the XAML to put an int in the XAML saves us from doing string lookups at runtime.
We use this for string resource lookups, colors, command ID lookups, etc.
Integrating non-XAML resources
One of our earliest changes was creating a custom build tool to perform simple transformations on XAML files.
inline IXamlType^ __XamlTypeInfo_CreateType_MyEnum(XamlTypeInfoProvider^ provider){ XamlUserType^ userType = ref new XamlUserType(provider, L"MyEnum",
provider->GetXamlTypeByName(L"System.Enum")); userType->KindOfType = Interop::TypeKind::Metadata; userType->FromStringConverter = [](XamlUserType^ userType, String^ input) -> Object^ { uint32 enumValue = userType->CreateEnumUIntFromString(input); return ref new Platform::Box<MyEnum>((MyEnum)enumValue); }; userType->AddEnumValue(L"Option1", PropertyValue::CreateInt32(MyEnum::Option1)); userType->AddEnumValue(L"Option2", PropertyValue::CreateInt32(MyEnum::Option2)); return userType;}__XamlTypeInfoMap_IXamlType(L"MyEnum", __XamlTypeInfo_CreateType_MyEnum);
Why not put it all in an enum?
Icon control is a UserControl with no XAML file• All contents are generated via code.• Internals are abstracted from users.
We switched to colored fonts instead of images• Glyphs now support colored fonts and are more efficient than
TextBlock.• This allows a single asset to support multiple sizes.• Can still use PNGs for targeted cases.
public ref class Icon sealed : public UserControl{public: property uint16 Id { uint16 get(); void set… property IconSize Size { IconsSize get(); void set…};
Office Icon control
How we load icons for commands in Office.
Icon lookup logic<core:Icon Id=“{= msotcidFoo}” Size=“{= ImageSize::Square20}”/>
<core:Icon>
Look up ID metadata
Use primary font
Use language
specific font
control boot
CreateGlyphs
CreateImage
Get target image file
Insert in Icon.Conten
t
What about very custom UI?
Highly sophisticated, dynamic, animated, interactive content.• Rendered using DirectX since Office 2013.• Content is rendered in 2d layers which are then
composed.
XAML has a great solution here!• SurfaceImageSource, VirtualSurfaceImageSource
and SwapChainPanel enable integrating DirectX content.
• Can be transparently composed with XAML UI.• Allows for seamless animations and smooth
scrolling.
Office document canvases
The architectural approach to the document canvases where fidelity of look and feel is absolutely critical.
Application Thread
UI Thread Compositor Thread
Rendering Cross Platform Architecture
Channel
WinRT BackendXAML
Direct Compositio
n
Channel
Android BackendJava Views
Channel
In-Process Direct3D
Compositor
iOS & MacFrontend talks directly with Core Animation
Core Animation
Win32
XAML
Android
iOS OSX
Shared C++Layout
Rendering
AirSpace Frontend
Some Code
Sharing +
Platform-Specific Forking
• All animations should run on the composition thread - independent of UI
Animating our UI
ThemeTransitions• Easy• Implicit• Limited customization
Storyboards• Fully customizable• Verbose• Difficult to make
consistent• Require Custom
Triggers
Animation panel demo
Flexibility and power of storyboards• Our animations can use any timing curve.• Multiple KeyFrames.
Uses Office’s existing animation definitions• Office defines a set of animation types as an
AnimationClass.• Animation definitions can then be shared across
platforms.
Animations triggered on reposition, show and hide
Integrating animations into a custom panel Office Panel creates implicit, fluid, flexible and interruptible animations.
Developers can customize animations through custom and attached properties• The animation is determined by the AnimationClass
property on the panel.• Or from an AnimationClassOverride attached
property on the specific element.
Our Panel implements MeasureOverride and ArrangeOverrideDynamically generates storyboards during arrange
Implicit animations
Triggering animation automatically makes creating fluid UI easier
child.Arrange(newPos);
Storyboard s = new Storyboard();DoubleAnimation daX = new DoubleAnimation() { From = oldPos.X – newPos.X, To = 0 };DoubleAnimation daY = new DoubleAnimation() { From = oldPos.Y – newPos.Y, To = 0 };Storyboard.SetTarget(daX, child.RenderTransform);Storyboard.SetTarget(daY, child.RenderTransform);Storyboard.SetTargetProperty(daX, "X");Storyboard.SetTargetProperty(daY, "Y");s.Children.Add(daX);s.Children.Add(daY);s.Begin();
What happens if the position changes while an animation is in progress?
Animating from old position to new
Positioning content entirely with RenderTransform
child.Arrange({0, 0}, finalSize);Storyboard s = new Storyboard();DoubleAnimation daX = new DoubleAnimation() { To = newPos.X };DoubleAnimation daY = new DoubleAnimation() { To = newPos.Y };Storyboard.SetTarget(daX, child.RenderTransform);Storyboard.SetTarget(daY, child.RenderTransform);Storyboard.SetTargetProperty(daX, "X");Storyboard.SetTargetProperty(daY, "Y");s.Children.Add(daX);s.Children.Add(daY);s.Begin();
Accounting for interruptions
No From Specifie
d
Always arrang
e at 0,0
Normally resize doesn’t animate in XAML• To animate resize you have to turn on UseDependantAnimations.• Could use ScaleTransform but that stretches the content.
Our solution: split content into multiple layers• All our items have been arranged with RenderTransforms.• Use ScaleTransform on backgrounds and borders.• Allow content to animate to new layout.• Triggered during arrange in response to layout changes.
Resize animations
Word, Excel, PowerPoint, OneNote and the new Mail and Calendar apps are shipping soon.• They are built on the public platform.• Still work to do but they are looking great.
The Win10 platform improvements will make them even better!• We are working on integrating new platform features right now.
We invented our own databinding replacement…• But if x:Bind had existed we wouldn’t have felt the need.
Universal Office applications today
• One package for mobile and desktop.
• Adapt to changes on the fly.• Use consistent crossover
points.• Layer things appropriately.
• Keep child elements self-contained.• The switch often just involves laying them out
differently.• Build external communication around what you
want it to do, not how it is set up.
They are truly universal apps
This code is not based on a fork.It is the same tree that produces Win32 Office and our web applications.It now also includes the Android tablet versions of Word, Excel and PowerPoint.
Universal is built out of our main tree
96%
4%
Universal
Shared Endpoint
95%
5%Android
Shared Endpoint
• Universal is built from
• 96% shared code (by LoC)• including the XAML files and related
code-behind. • If you exclude the XAML pieces, then
Universal is built from:
• 98.6% shared code.
• Android is built from
• 95% shared code (by LoC)• including the Java files and related code-
behind.• If you exclude the Java pieces, then
Android is builtfrom:
• 99.7% shared code.
So how much code are we sharing?
• XAML Session 635: Data Binding: Boost Your App’s Performance Through New Enhancements to XAML Data Binding
• XAML Session 689: XAML Performance: Techniques for maximizing Universal App experiences build with XAML
• Microsoft Office Cross-Platform Architecture - @Scale 2014
Call to Action
© 2015 Microsoft Corporation. All rights reserved.