<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>WPF on AYU MAX</title>
    <link>https://ayumax.net/tags/wpf/</link>
    <description>Recent content in WPF on AYU MAX</description>
    <image>
      <title>AYU MAX</title>
      <url>https://ayumax.net/profile.jpg</url>
      <link>https://ayumax.net/profile.jpg</link>
    </image>
    <generator>Hugo -- 0.147.1</generator>
    <language>en</language>
    <lastBuildDate>Mon, 13 May 2019 19:05:18 +0000</lastBuildDate>
    <atom:link href="https://ayumax.net/tags/wpf/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Trying out .NET Core 3.0 WPF (Made a Node-Based Calculator)</title>
      <link>https://ayumax.net/entry/2019/05/13/190518/</link>
      <pubDate>Mon, 13 May 2019 19:05:18 +0000</pubDate>
      <guid>https://ayumax.net/entry/2019/05/13/190518/</guid>
      <description>&lt;h1 id=&#34;tried-it-out&#34;&gt;Tried It Out&lt;/h1&gt;
&lt;p&gt;With the combination of Visual Studio 2019 + .NET Core 3.0, WPF is usable, so I created a new project and tried it out.&lt;/p&gt;
&lt;p&gt;For now, I&amp;rsquo;ll try writing as if I&amp;rsquo;m using the traditional WPF, forgetting that I&amp;rsquo;m using .NET Core, and see how it feels.&lt;/p&gt;
&lt;p&gt;Usage methods are introduced by various people, but you can use it by installing Visual Studio 2019 and .NET Core 3.0, and then checking ON &amp;ldquo;Use preview .NET Core SDK&amp;rdquo; in Visual Studio options.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<h1 id="tried-it-out">Tried It Out</h1>
<p>With the combination of Visual Studio 2019 + .NET Core 3.0, WPF is usable, so I created a new project and tried it out.</p>
<p>For now, I&rsquo;ll try writing as if I&rsquo;m using the traditional WPF, forgetting that I&rsquo;m using .NET Core, and see how it feels.</p>
<p>Usage methods are introduced by various people, but you can use it by installing Visual Studio 2019 and .NET Core 3.0, and then checking ON &ldquo;Use preview .NET Core SDK&rdquo; in Visual Studio options.</p>
<p>I think this check will become unnecessary once .NET Core 3.0 becomes the official version.</p>
<figure>
    <img loading="lazy" src="/images/2019/05/13/190518/20190512064056.png"/> 
</figure>

<h1 id="c-80-nullable-reference-types">C# 8.0 Nullable Reference Types</h1>
<p>At the same time, I also tried the nullable reference types introduced in C# 8.0.</p>
<p>This time, since I&rsquo;m trying it out by creating a new project, I&rsquo;ve enabled nullable reference types for the entire project by describing it in the .csproj as follows.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;propertygroup&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;outputtype&gt;</span>WinExe<span style="color:#f92672">&lt;/outputtype&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;targetframework&gt;</span>netcoreapp3.0<span style="color:#f92672">&lt;/targetframework&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;usewpf&gt;</span>true<span style="color:#f92672">&lt;/usewpf&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;langversion&gt;</span>8.0<span style="color:#f92672">&lt;/langversion&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;nullablecontextoptions&gt;</span>enable<span style="color:#f92672">&lt;/nullablecontextoptions&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/propertygroup&gt;</span>
</span></span></code></pre></div><p>With this setting, you can no longer put null in the traditional way of writing reference types.
Also, it will now issue a warning when accessing a variable that might be null.</p>
<h1 id="what-i-made">What I Made</h1>
<p>Since I&rsquo;m creating a new project, I decided to set a theme.<br/>
Because I often see node-based creation environments in tools like <a href="https://www.unrealengine.com/en-US/what-is-unreal-engine-4">Unreal Engine (UE4)</a>, which I use at work, and <a href="https://www.sidefx.com/products/houdini/">Houdini</a>, which I&rsquo;ve been paying attention to recently, I chose &ldquo;Node-Based UI&rdquo; as the theme.</p>
<figure>
    <img loading="lazy" src="/images/2019/05/13/190518/20190513175842.png"/> 
</figure>

<p>Just connecting nodes isn&rsquo;t interesting, so I&rsquo;ve included calculator functionality.</p>
<p>What I&rsquo;ve made, as of writing this article, looks like this:</p>
<p>Currently, you can connect nodes to perform addition and subtraction.</p>
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ノードベース計算機を結構バージョンアップ。<br>・ノード間の線は3次ベジェ曲線に<br>・ノードのプロパティはポップアップで変更<br>・入力ピン数の動的変更<a href="https://twitter.com/hashtag/wpf?src=hash&amp;ref_src=twsrc%5Etfw">#wpf</a> <a href="https://twitter.com/hashtag/dotnetcore?src=hash&amp;ref_src=twsrc%5Etfw">#dotnetcore</a> <a href="https://t.co/2RFaWWyjXf">pic.twitter.com/2RFaWWyjXf</a></p>&mdash; ayuma (@ayuma_x) <a href="https://twitter.com/ayuma_x/status/1127130209390477312?ref_src=twsrc%5Etfw">May 11, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>The complete project is available on GitHub.</p>
<div class="github-card" data-user="ayumax" data-repo="NodeCalculator" data-width="400" data-height="" data-theme="default"></div>
<script src="https://cdn.jsdelivr.net/github-cards/latest/widget.js"></script>
<ul>
<li>Note: This node-based calculator is still under development, so it is frequently modified.</li>
</ul>
<h1 id="references-from-nuget">References from NuGet</h1>
<p>This time, I&rsquo;ve added references to the following four from NuGet:</p>
<ul>
<li>Extended.Wpf.Toolkit</li>
<li>gong-wpf-dragdrop</li>
<li>Microsoft.Xaml.Behaviors.Wpf</li>
<li>ReactiveProperty</li>
</ul>
<figure>
    <img loading="lazy" src="/images/2019/05/13/190518/20190513062057.png"/> 
</figure>

<p>Among these, <code>Extended.Wpf.Toolkit</code> and <code>Microsoft.Xaml.Behaviors.Wpf</code> don&rsquo;t seem to support .NET Core 3.0 yet, resulting in the following warnings:</p>
<pre tabindex="0"><code>NU1701: Package &#39;Extended.Wpf.Toolkit 3.5.0&#39; was restored using &#39;.NETFramework,Version=v4.6.1&#39; instead of the project target framework &#39;.NETCoreApp,Version=v3.0&#39;. This package may not be fully compatible with your project.
NU1701: Package &#39;Microsoft.Xaml.Behaviors.Wpf 1.0.1&#39; was restored using &#39;.NETFramework,Version=v4.6.1&#39; instead of the project target framework &#39;.NETCoreApp,Version=v3.0&#39;. This package may not be fully compatible with your project.
</code></pre><p>For now, there are no issues with the functionality I&rsquo;m using, and since .NET Core 3.0 is still a preview version, I&rsquo;ve decided not to worry about it.</p>
<p>If these warnings persist even after .NET Core 3.0 becomes the official version, actions such as handling it myself and building might be necessary.</p>
<h1 id="a-little-introduction-to-the-structure">A Little Introduction to the Structure</h1>
<p>I will focus on introducing the functionality for adding new nodes among what I created this time. (I thought about writing about drawing lines and performing calculations too, but since it&rsquo;s actively under development, there&rsquo;s a high possibility of future changes.)</p>
<h2 id="node-placement">Node Placement</h2>
<p>The XAML for the View where nodes are placed is as follows.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;itemscontrol</span> <span style="color:#a6e22e">itemsource=</span><span style="color:#e6db74">&#34;{Binding Nodes}&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">background=</span><span style="color:#e6db74">&#34;Transparent&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">dd:DragDrop.IsDropTarget=</span><span style="color:#e6db74">&#34;True&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">dd:DragDrop.DropHandler=</span><span style="color:#e6db74">&#34;{Binding}&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;itemscontrol.itemspanel&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;itemspaneltemplate&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;canvas</span> <span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/itemspaneltemplate&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/itemscontrol.itemspanel&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;itemscontrol.itemtemplate&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;datatemplate&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;node:NodeContainer</span> <span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/datatemplate&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/itemscontrol.itemtemplate&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;itemscontrol.itemcontainerstyle&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;style</span> <span style="color:#a6e22e">targettype=</span><span style="color:#e6db74">&#34;ContentPresenter&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;setter</span> <span style="color:#a6e22e">property=</span><span style="color:#e6db74">&#34;Canvas.Top&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;{Binding PositionY.Value}&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&lt;setter</span> <span style="color:#a6e22e">property=</span><span style="color:#e6db74">&#34;Canvas.Left&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;{Binding PositionX.Value}&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;/style&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/itemscontrol.itemcontainerstyle&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/itemscontrol&gt;</span>
</span></span></code></pre></div><p>By specifying Canvas for ItemsPanel, placement using absolute coordinates is enabled.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;itemscontrol.itemspanel&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;itemspaneltemplate&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;canvas</span> <span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/itemspaneltemplate&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/itemscontrol.itemspanel&gt;</span>
</span></span></code></pre></div><p>Node position setting is handled by binding Canvas.Top and Left to the properties of the node&rsquo;s ViewModel in ItemContainerStyle.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;itemscontrol.itemcontainerstyle&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;style</span> <span style="color:#a6e22e">targettype=</span><span style="color:#e6db74">&#34;ContentPresenter&#34;</span><span style="color:#f92672">&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;setter</span> <span style="color:#a6e22e">property=</span><span style="color:#e6db74">&#34;Canvas.Top&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;{Binding PositionY.Value}&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;setter</span> <span style="color:#a6e22e">property=</span><span style="color:#e6db74">&#34;Canvas.Left&#34;</span> <span style="color:#a6e22e">value=</span><span style="color:#e6db74">&#34;{Binding PositionX.Value}&#34;</span><span style="color:#f92672">/&gt;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;/style&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">&lt;/itemscontrol.itemcontainerstyle&gt;</span>
</span></span></code></pre></div><p>Also, since node generation via drag-and-drop is incorporated into this ItemsControl, <code>dd:DragDrop.IsDropTarget</code> and <code>dd:DragDrop.DropHandler</code> are specified.</p>
<p>This is a feature of <code>gong-wpf-dragdrop</code> added via NuGet; writing it this way allows you to write the drop handling logic in the ViewModel bound to the ItemsControl.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-xml" data-lang="xml"><span style="display:flex;"><span><span style="color:#f92672">&lt;itemscontrol</span> <span style="color:#a6e22e">itemsource=</span><span style="color:#e6db74">&#34;{Binding Nodes}&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">background=</span><span style="color:#e6db74">&#34;Transparent&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">dd:DragDrop.IsDropTarget=</span><span style="color:#e6db74">&#34;True&#34;</span> 
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">dd:DragDrop.DropHandler=</span><span style="color:#e6db74">&#34;{Binding}&#34;</span>
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&gt;</span>
</span></span></code></pre></div><h2 id="new-node-generation">New Node Generation</h2>
<p>The following source code is the process when dropping onto the View where nodes are placed.</p>
<p><code>dropInfo.Data</code> contains the DataContext (bound ViewModel) of the dropped View, so the process changes depending on its type.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cs" data-lang="cs"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">void</span> Drop(IDropInfo dropInfo)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">switch</span> (dropInfo.Data)
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> NodeViewModel node:
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>                nowDragItem = <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> NodeConnectionViewModel connection:
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>                connection.LineToX.Value = <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>                connection.LineToY.Value = <span style="color:#ae81ff">0</span>;
</span></span><span style="display:flex;"><span>                connection.Visible.Value = Visibility.Hidden;
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> ToolBoxItemViewModel item:
</span></span><span style="display:flex;"><span>            {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">var</span> newItem = Activator.CreateInstance(item.NodeModelType) <span style="color:#66d9ef">as</span> NodeBase;
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> (newItem != <span style="color:#66d9ef">null</span>)
</span></span><span style="display:flex;"><span>                {
</span></span><span style="display:flex;"><span>                    newItem.PositionX = dropInfo.DropPosition.X;
</span></span><span style="display:flex;"><span>                    newItem.PositionY = dropInfo.DropPosition.Y;
</span></span><span style="display:flex;"><span>                    mainModel.Nodes.Add(newItem);
</span></span><span style="display:flex;"><span>                }                
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">default</span>:
</span></span><span style="display:flex;"><span>            nowDragItem = <span style="color:#66d9ef">null</span>;
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>;
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The Drop process when creating a new node is the part that handles <code>ToolBoxItemViewModel</code>.</p>
<p>The dropped ViewModel has a property called NodeModelType of type Type, and based on this, an instance of the node is generated.</p>
<p>The generated node is given coordinates based on the drop position and added to the model&rsquo;s <code>Nodes</code> array.</p>
<p>This addition to the array propagates from Model -&gt; ViewModel -&gt; View, and the new node is displayed in the ItemsControl explained earlier.</p>
<p>Also, where <code>Activator.CreateInstance</code> is used, I initially skipped the null check out of laziness, but since the nullable reference types feature was ON, a warning appeared.</p>
<p>Adding the null check made the warning disappear, apparently because the compiler performs flow analysis. Amazing!</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-cs" data-lang="cs"><span style="display:flex;"><span><span style="color:#66d9ef">case</span> ToolBoxItemViewModel item:
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> newItem = Activator.CreateInstance(item.NodeModelType) <span style="color:#66d9ef">as</span> NodeBase;
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (newItem != <span style="color:#66d9ef">null</span>) <span style="color:#75715e">// Null check added</span>
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            newItem.PositionX = dropInfo.DropPosition.X;
</span></span><span style="display:flex;"><span>            newItem.PositionY = dropInfo.DropPosition.Y;
</span></span><span style="display:flex;"><span>            mainModel.Nodes.Add(newItem);
</span></span><span style="display:flex;"><span>        }                    
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">break</span>;
</span></span></code></pre></div><h1 id="summary">Summary</h1>
<p>While writing this article, I intended to summarize any points like &ldquo;you should be careful about this when using WPF with .NET Core,&rdquo; but&hellip; there weren&rsquo;t any in particular.</p>
<p>You can use it just like when using the previous .NET Framework version of WPF.</p>
<p>It was said that the inability to use the XAML designer in the .NET Core version was a disadvantage, but it seems it became usable in VS2019 Preview 3 (haven&rsquo;t tried it yet), so I think this will also cease to be an issue eventually.</p>
<p>Also, regarding nullable reference types, I initially thought the warnings might be intrusive, but that wasn&rsquo;t the case. Instead, I felt it was a helpful feature because it points out places where I might have overlooked potential null access.</p>
<p>Introducing it into existing projects might result in a flood of warnings and be difficult, but I think enabling it for newly created projects enhances code safety.</p>
<p>I believe .NET Core will become mainstream for future C# development, but I felt that developing desktop applications using WPF is also perfectly fine.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
