質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

3105閲覧

WPFで、Widthプロパティのアニメーション幅を自動計算したい

tails

総合スコア22

.NET Core

.NET Coreは、マネージソフトウェアフレームワークでオープンソースで実装されています。クロスプラットフォームを前提に考えられており、Windows/Mac/Linuxで動くアプリケーションを作成することが可能です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

0クリップ

投稿2021/11/26 15:56

実現出来ていること

XAMLのみを用いて、画像のようなアニメーションをするサイドメニューを作りました。
緑の丸と紫の丸は、今はただの円ですが、メニューアイコンにする予定です。(つまり Image 要素)
画像のすぐ下にソースコードも載せます。
サイドメニューのアニメーションの様子

XAML

1<Window x:Class="SideBar.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:SideBar" mc:Ignorable="d" Title="MainWindow" 7 Height="450" Width="800"> 8 <DockPanel> 9 <StackPanel DockPanel.Dock="Left" Width="53" Background="DarkGray"> 10 <StackPanel.Triggers> 11 <EventTrigger RoutedEvent="MouseEnter" SourceName="side_menu_panel"> 12 <BeginStoryboard> 13 <Storyboard> 14 <DoubleAnimation Storyboard.TargetProperty="Width" 15 From="{Binding Path=Width, RelativeSource={RelativeSource Self}}" 16 To="145" Duration="0:0:0.25" /> 17 </Storyboard> 18 </BeginStoryboard> 19 </EventTrigger> 20 <EventTrigger RoutedEvent="MouseLeave" SourceName="side_menu_panel"> 21 <BeginStoryboard> 22 <Storyboard> 23 <DoubleAnimation Storyboard.TargetProperty="Width" 24 From="{Binding Path=Width, RelativeSource={RelativeSource Self}}" 25 To="53" Duration="0:0:0.25" /> 26 </Storyboard> 27 </BeginStoryboard> 28 </EventTrigger> 29 </StackPanel.Triggers> 30 <StackPanel.Resources> 31 <Style TargetType="RadioButton"> 32 <Setter Property="Background" Value="Transparent" /> 33 <Setter Property="Padding" Value="4" /> 34 <Setter Property="VerticalContentAlignment" Value="Center" /> 35 </Style> 36 <Style TargetType="ContentPresenter" x:Key="MarginLeft"> 37 <Setter Property="Margin" Value="20,0,0,0" /> 38 <!--<Setter Property="Visibility" Value="Collapsed" />--> 39 </Style> 40 </StackPanel.Resources> 41 42 <!-- menu items --> 43 <StackPanel x:Name="side_menu_panel"> 44 <RadioButton Content="メニュー1"> 45 <RadioButton.Template> 46 <ControlTemplate TargetType="RadioButton"> 47 <!-- Set background so that it can be selected. --> 48 <Border Background="{TemplateBinding Background}" 49 Padding="{TemplateBinding Padding}" Cursor="Hand"> 50 <BulletDecorator> 51 <BulletDecorator.Bullet> 52 <Ellipse Width="45" Height="45" Fill="#FF21E402" /> 53 </BulletDecorator.Bullet> 54 <ContentPresenter Style="{StaticResource MarginLeft}" 55 VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> 56 </BulletDecorator> 57 </Border> 58 </ControlTemplate> 59 </RadioButton.Template> 60 </RadioButton> 61 <RadioButton Content="メニュー2"> 62 <RadioButton.Template> 63 <ControlTemplate TargetType="RadioButton"> 64 <!-- Set background so that it can be selected. --> 65 <Border Background="{TemplateBinding Background}" 66 Padding="{TemplateBinding Padding}" Cursor="Hand"> 67 <BulletDecorator> 68 <BulletDecorator.Bullet> 69 <Ellipse Width="45" Height="45" Fill="MediumVioletRed" /> 70 </BulletDecorator.Bullet> 71 <ContentPresenter Style="{StaticResource MarginLeft}" 72 VerticalAlignment="{TemplateBinding VerticalContentAlignment}" /> 73 </BulletDecorator> 74 </Border> 75 </ControlTemplate> 76 </RadioButton.Template> 77 </RadioButton> 78 </StackPanel> 79 </StackPanel> 80 81 <!-- main panel --> 82 <Rectangle DockPanel.Dock="Right" Fill="Gray" /> 83 </DockPanel> 84</Window>

実現したいこと

ソースコード中に、手計算で算出した数値がいくつかありますが、それらは自動的に算出させて、手計算を不要にしたいです。
但し、コードビハインド等を使って C# から計算するのではなく、XAMLのみで実現したいです。

自動計算させたい数値は、具体的には、

  • StackPanel の初期値 Width = 53
  • DoubleAnimation の To = 145 と To = 53

です。

メニューが閉じている時の幅は、「メニュー1」「メニュー2」の Visibility が Collapsed の状態で、StackPanel の Width を設定しなかったとき、自動的に計算される Width の値で計算できます。
つまり、手計算だと、4 + 45 + 4 = 53 です。(4 = RadioButton の Padding, 45 = Ellipse の Width)
メニューが開いている時の幅は、「メニュー1」「メニュー2」の Visibility が Visible の状態で、StackPanel の Width を設定しなかったとき、自動的に計算される Width の値で計算できます。
こっちは手計算はよく分からなかったのでやってません。とりあえず、145 で収まったのでそれにしただけです。

留意点

同じ動作が実現できるのであれば、使っているコントロールなどは自由に変えれます。
自分でコントロールをカスタムして作成する必要がある場合は、C#を使わないといけないと思いますので、その際はC#を使っても大丈夫です。
現状メニューとしてクリック可能な領域は、それぞれ以下の赤と青の矩形で示した領域の内側(境界線含む)です。
この動作についても現状のままとしたいです。
(したがって、アイコンの部分と「メニュー1」の部分を別のボタンなどにして分けるようなことはしたくないです)
選択可能領域

ご回答、よろしくお願い致します。

環境

Windows 10
Visual Studio 2019
.NET Core 3.1
WPF

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hihijiji

2021/11/27 02:32

XAMLのみで… 気持ちはよくわかります。 でもWPFのXAMLは四則演算すら出来ないので、こだわるとどんどん面倒なことになります。
tails

2021/11/27 03:52

TN8001さんに頂いた回答のような、Converter を使って行うなど、まぁとにかく 53 みたいな、マジックナンバー的なのがなくなれば大丈夫です。
guest

回答1

0

ベストアンサー

ソースコード中に、手計算で算出した数値がいくつかありますが、それらは自動的に算出させて、手計算を不要にしたいです。

よくあるのはDoubleAnimationは、0~1の比率としてアニメーションさせる手法です(そのかわりConverterが必要になります)
WPF animation: binding to the "To" attribute of storyboard animation - Stack Overflow

比率でやったとしても基準になる数値は必要になりますが、お手軽なのはダミーで実際に作ってしまうことです(Collapsedにしてしまうとサイズ0になってしまうので、Hiddenにしたり何かの裏に隠します^^;

xml

1<Window 2 x:Class="Questions371156.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Questions371156" 6 Width="800" 7 Height="450"> 8 <Window.Resources> 9 <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> 10 <local:MultiplyConverter x:Key="multiplyConverter" /> 11 </Window.Resources> 12 <DockPanel> 13 <StackPanel Background="DarkGray"> 14 <StackPanel.Width> 15 <!-- 比率が0~1にアニメーションすると、StackPanel.Widthが53~145に変化する(ようなコンバータを書く) --> 16 <MultiBinding Converter="{StaticResource multiplyConverter}"> 17 <!-- 初期値: 53の部分 --> 18 <Binding ElementName="dummy_min_button" Path="ActualWidth" /> 19 <!-- ターゲット値: 145の部分 --> 20 <Binding ElementName="side_menu_panel2" Path="DesiredSize.Width" /> 21 <!-- 比率: 0~1で変化する --> 22 <Binding ElementName="dummy_width_ratio" Path="Width" /> 23 </MultiBinding> 24 </StackPanel.Width> 25 26 <StackPanel.Triggers> 27 <EventTrigger RoutedEvent="MouseEnter" SourceName="side_menu_panel2"> 28 <BeginStoryboard> 29 <Storyboard> 30 <DoubleAnimation 31 Storyboard.TargetName="dummy_width_ratio" 32 Storyboard.TargetProperty="Width" 33 To="1" 34 Duration="0:0:0.3"> 35 <DoubleAnimation.EasingFunction> 36 <CubicEase /> 37 </DoubleAnimation.EasingFunction> 38 </DoubleAnimation> 39 </Storyboard> 40 </BeginStoryboard> 41 </EventTrigger> 42 <EventTrigger RoutedEvent="MouseLeave" SourceName="side_menu_panel2"> 43 <BeginStoryboard> 44 <Storyboard> 45 <DoubleAnimation 46 Storyboard.TargetName="dummy_width_ratio" 47 Storyboard.TargetProperty="Width" 48 To="0" 49 Duration="0:0:0.15"> 50 <DoubleAnimation.EasingFunction> 51 <CubicEase /> 52 </DoubleAnimation.EasingFunction> 53 </DoubleAnimation> 54 </Storyboard> 55 </BeginStoryboard> 56 </EventTrigger> 57 </StackPanel.Triggers> 58 <StackPanel.Resources> 59 <Style TargetType="RadioButton"> 60 <Setter Property="Padding" Value="4" /> 61 <Setter Property="VerticalContentAlignment" Value="Center" /> 62 <Setter Property="Template"> 63 <Setter.Value> 64 <ControlTemplate TargetType="RadioButton"> 65 <Border 66 Padding="{TemplateBinding Padding}" 67 Background="Transparent" 68 Cursor="Hand"> 69 <BulletDecorator> 70 <BulletDecorator.Bullet> 71 <!-- Background使ってなさそうなのでこっちで使いました^^; --> 72 <Ellipse 73 Width="45" 74 Height="45" 75 Fill="{TemplateBinding Background}" /> 76 </BulletDecorator.Bullet> 77 <ContentPresenter 78 x:Name="contentPresenter" 79 Margin="20,0,0,0" 80 VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 81 Visibility="{TemplateBinding HasContent, Converter={StaticResource BooleanToVisibilityConverter}}" /> 82 </BulletDecorator> 83 </Border> 84 </ControlTemplate> 85 </Setter.Value> 86 </Setter> 87 </Style> 88 </StackPanel.Resources> 89 <!-- ダミーを隠すためにGridで重なってもらう --> 90 <Grid> 91 <!-- 閉じたときのサイズ計算用ダミー --> 92 <RadioButton x:Name="dummy_min_button" HorizontalAlignment="Left" /> 93 <!-- 比率計算用ダミー --> 94 <Border x:Name="dummy_width_ratio" Width="0" /> 95 96 <StackPanel x:Name="side_menu_panel2"> 97 <RadioButton Background="#FF21E402" Content="メニュー1" /> 98 <RadioButton Background="MediumVioletRed" Content="メニューーー2" /> 99 </StackPanel> 100 </Grid> 101 </StackPanel> 102 <Rectangle Fill="Gray" /> 103 </DockPanel> 104</Window>

cs

1using System; 2using System.Globalization; 3using System.Windows; 4using System.Windows.Data; 5 6namespace Questions371156 7{ 8 public class MultiplyConverter : IMultiValueConverter 9 { 10 // values[0]:初期値 values[1]:ターゲット値 values[2]:比率 が入力される前提 11 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 12 => (double)values[0] + (double)values[2] * ((double)values[1] - (double)values[0]); 13 14 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException(); 15 } 16 17 public partial class MainWindow : Window 18 { 19 public MainWindow() => InitializeComponent(); 20 } 21}

アプリ動画


アニメーションは「引っ込むときは速くする・イージングをつける」と、オサレな感じになると思います^^

WPF用ではありませんがUXのガイドラインがあります。
モーション (Windows アプリでのアニメーション) - Windows apps | Microsoft Docs

投稿2021/11/27 02:27

編集2023/07/29 09:54
TN8001

総合スコア9317

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tails

2021/11/27 04:13 編集

回答ありがとうございます。 なるほど、ダミーを置いておいて、そこから取るんですね。。 あと、比率計算用の変数的なものもダミーでおくということですね。 MultiBinding で掛け算をさせられることとか、Animation の From を指定しなかったら現在値からアニメーションしてくれるだとか、知らなかったことばかりでとても参考になりました。 ところで、Border につけていた background には理由があって、 <!-- Set background so that it can be selected. --> <Border Background="{TemplateBinding Background}" と書いていたように、(自分の環境だけなのか分かりませんが)ここに background を付けておかないと、メニューの矩形領域全体が選択可能にならない(つまり、円の外側とか、ContentPresenter の外側とかの、背景が透過して見えている部分は、マウスを乗せても、ラジオボタンではなく、背景の StackPanel と認識される)ので、background を付けていました。 透明(というか、background が未設定?)だと、MouseEnter, MouseLeave とかのマウス系のイベントも透過する感じなんでしょうか。 また、RadioButton の Template をリソースにしていなかったのにも理由があって、今は Ellipse で書いていますが、将来的には Image でアイコンにする予定です。 そうなると Source プロパティにファイルパスを指定することになると思うのですが、RadioButton の方にバインディングしても違和感のないプロパティが無さげなので、仕方なく個別に書いていました。 (この件に関しては別で色々試して、また質問する予定でしたが…) 今のところは、添付プロパティ辺りでいい感じにやれそうかな?と思っているだけで実現できていなかったので、個別に書いていました。
TN8001

2023/07/29 10:01 編集

> 透明(というか、background が未設定?)だと、MouseEnter, MouseLeave とかのマウス系のイベントも透過する感じなんでしょうか。 未指定(null)だとマウスも透過してしまいます。Transparentだと透過しません。 ちょっとワナっぽいですねw > RadioButton の方にバインディングしても違和感のないプロパティが無さげなので、仕方なく個別に書いていました。 RadioButtonを継承してDependencyPropertyを、好きなだけ作っちゃうのが楽なんじゃないですかね^^;
tails

2021/11/29 03:46

> 未指定(null)だとマウスも透過してしまいますね。 null と Transparent でまた違うんですね… > RadioButtonを継承してDependencyPropertyを、好きなだけ作っちゃうのが楽なんじゃないですかね^^; それもアリですね… ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問