WPFオープンソースプロジェクト:WPF-ControlBase

WPFオープンソースプロジェクト:WPF-ControlBase

リポジトリのREADMEはとても素朴ですが、作者のREADMEに貼られたいくつかのブログ記事を見ると、気に入るでしょう。

最終更新 2021/11/30 16:57
He BianGu
読了目安 27 分
カテゴリ
WPF
テーマ
WPFコントロールライブラリ WPFオープンソースプロジェクト
タグ
.NET WPF WPFオープンソースプロジェクト オープンソースプロジェクト オープンソース

仓库截图

リポジトリの README はシンプルですが、作者の README に貼られたブログ記事を読めば気に入るはずです。余計な話は抜きにして、目次からご紹介します。

  1. アニメーションのラッピング
https://blog.csdn.net/u010975589/article/details/95974854
  1. プロパティフォーム
https://blog.csdn.net/u010975589/article/details/95970200
  1. メッセージダイアログ
https://blog.csdn.net/u010975589/article/details/95985190
  1. WPF で MVC を適用する
https://blog.csdn.net/u010975589/article/details/100019431
  1. その他機能の説明
https://blog.csdn.net/u010975589/article/details/103083605

以下、詳細に説明します。

1. アニメーションのラッピング

原文タイトル:サンプル:WPF におけるカスタム StoryBoarService による StoryBoard・Animation のラッピングでアニメーション記述の簡略化
原文リンク:https://blog.csdn.net/u010975589/article/details/95974854

1.1 目的:StoryBoard と Animation をラッピングすることでアニメーション記述を簡略化すること

1.2 サンプル

説明:フェードアウトは WPF でよく使われるアニメーションです。上図は StoryBoarService でラッピングした効果で、コード内で以下の 1 行を実行するだけで実現できます。

DoubleStoryboardEngine.Create(1, 0, 1, "Opacity").Start(element);

上記のクローズ効果は以下のようにコマンドを定義できます。

public class CollapsedOfOpacityCommand : ICommand
{

    public bool CanExecute(object parameter) => true;

    public void Execute(object parameter)
    {
        if(parameter is UIElement element)
        {
            var engine = DoubleStoryboardEngine.Create(1, 0, 1, "Opacity");

            engine.Start(element);
        }
    }

    public event EventHandler CanExecuteChanged;
}

Xaml で以下のコマンドを呼び出すだけで、クローズ&フェードアウト効果が完了します。

Command="{x:Static base:CommandService.CollapsedOfOpacityCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource
AncestorType=GroupBox}}"

渡された CommandParameter は、コマンド実行時にフェードアウトします。

アニメーション効果のコードは 1 行だけで済み、コード内の煩雑なアニメーションコーディングを簡略化します。

DoubleStoryboardEngine.Create(1, 0, 1, "Opacity").Start(element);

1.3 コード:

現時点では DoubleAnimation のラッピングのみ実装しています。今後、他のタイプもラッピング予定です。

1.3.1 変更不可な基底クラス

/// <summary> アニメーションエンジン基底クラス </summary>
public abstract class StoryboardEngineBase : IDisposable
{
    protected Storyboard storyboard = new Storyboard();

    public EventHandler CompletedEvent { get; set; }

    public EasingFunctionBase Easing { get; set; } = EasingFunctionFactroy.PowerEase;

    public PropertyPath PropertyPath { get; set; }

    public Duration Duration { get; set; }

    public void Dispose()
    {
        storyboard.Completed -= CompletedEvent;
    }

    public abstract StoryboardEngineBase Start(UIElement element);

    public abstract StoryboardEngineBase Stop();

    public StoryboardEngineBase(int second, string property)
    {
        this.PropertyPath = new PropertyPath(property);
        this.Duration = new Duration(TimeSpan.FromSeconds(second));
    }

}

/// <summary> アニメーションジェネリックエンジン基底クラス </summary>
public abstract class StoryboardEngineBase<T> : StoryboardEngineBase
{
    public StoryboardEngineBase(T from, T to, int second, string property) : base(second, property)
    {
        this.FromValue = from;
        this.ToValue = to;
    }

    public T FromValue { get; set; }

    public T ToValue { get; set; }

    //public RepeatBehavior RepeatBehavior { get; set; };

}

1.3.2 拡張可能な DoubleStoryboardEngine

/// <summary> DoubleAnimationアニメーションエンジン </summary>
public class DoubleStoryboardEngine : StoryboardEngineBase<double>
{
    public static DoubleStoryboardEngine Create(double from, double to, int second, string property)
    {
        return new DoubleStoryboardEngine(from, to, second, property);
    }

    public DoubleStoryboardEngine(double from, double to, int second, string property) : base(from, to, second, property)
    {

    }

    public override StoryboardEngineBase Start(UIElement element)
    {
        //  Do:タイムライン
        DoubleAnimation animation = new DoubleAnimation(1, 0, this.Duration);

        if (this.Easing != null)
            animation.EasingFunction = this.Easing;

        //if (this.RepeatBehavior != default(RepeatBehavior))
        //    animation.RepeatBehavior = (RepeatBehavior);

        //  Do:プロパティアニメーション
        storyboard.Children.Add(animation);
        Storyboard.SetTarget(animation, element);
        Storyboard.SetTargetProperty(animation, this.PropertyPath);

        if (CompletedEvent != null)
            storyboard.Completed += CompletedEvent;
        storyboard.Begin();

        return this;
    }

    public override StoryboardEngineBase Stop()
    {
        this.storyboard.Stop();

        return this;
    }
}

1.3.3 イージング関数ファクトリ

/// <summary> 説明:https://docs.microsoft.com/zh-cn/dotnet/framework/wpf/graphics-multimedia/easing-functions </summary>
public static class EasingFunctionFactroy
{
    /// <summary> PowerEase:公式 f(t) = t^p(p は Power プロパティ)を使用した加速・減速のアニメーションを作成します。 </summary>
    public static PowerEase PowerEase { get; set; } = new PowerEase();
    /// <summary> BackEase:アニメーション開始前に少し戻る動作を作成します。 </summary>
    public static BackEase BackEase { get; set; } = new BackEase();
    /// <summary> ElasticEase:バネのように振動しながら静止するアニメーションを作成します。 </summary>
    public static ElasticEase ElasticEase { get; set; } = new ElasticEase();
    /// <summary> BounceEase:バウンド効果を作成します。 </summary>
    public static BounceEase BounceEase { get; set; } = new BounceEase();
    /// <summary> CircleEase:円関数を使用した加速・減速のアニメーションを作成します。 </summary>
    public static CircleEase CircleEase { get; set; } = new CircleEase();

    /// <summary> QuadraticEase:公式 f(t) = t^2 を使用した加速・減速のアニメーションを作成します。 </summary>
    public static QuadraticEase QuadraticEase { get; set; } = new QuadraticEase();

    /// <summary> CubicEase:公式 f(t) = t^3 を使用した加速・減速のアニメーションを作成します。 </summary>
    public static CubicEase CubicEase { get; set; } = new CubicEase();
    /// <summary> QuarticEase:公式 f(t) = t^4 を使用した加速・減速のアニメーションを作成します。 </summary>
    public static QuarticEase QuarticEase { get; set; } = new QuarticEase();
    /// <summary> QuinticEase:公式 f(t) = t^5 を使用した加速・減速のアニメーションを作成します。 </summary>
    public static QuinticEase QuinticEase { get; set; } = new QuinticEase();

    /// <summary> ExponentialEase:指数関数を使用した加速・減速のアニメーションを作成します。 </summary>
    public static ExponentialEase ExponentialEase { get; set; } = new ExponentialEase();

    /// <summary> SineEase:三角関数(sine)を使用した加速・減速のアニメーションを作成します。 </summary>
    public static SineEase SineEase { get; set; } = new SineEase();

}

1.3.4 使用方法

/// <summary> コンストラクタ </summary>
/// <param name="from"> 開始値</param>
/// <param name="to"> 終了値 </param>
/// <param name="second"> 間隔時間(秒) </param>
/// <param name="property"> 変更するプロパティ名 </param>
///
public static DoubleStoryboardEngine Create(double from, double to, int second, string property)
{
    return new DoubleStoryboardEngine(from, to, second, property);
}

2. プロパティフォーム

原文タイトル:サンプル:WPF で開発したシンプル ObjectProperyForm によるエンティティフォームのバインディング
原文リンク:https://blog.csdn.net/u010975589/article/details/95970200

2.1 目的:カスタムコントロールでエンティティデータを直接バインドし、開発サイクルを短縮する

2.2 実装

  1. エンティティオブジェクトのバインド
  2. 属性(Attribute)によるプロパティ名の表示
  3. 属性によるバリデーション条件の追加
  4. String、Int、Double、DateTime、Bool の簡単な型に対する DataTemplate テンプレートを実装済み。その他のテンプレートは拡張可能
  5. その他随時更新...

2.3 サンプル

エンティティの定義は以下の通りです。

public class Student
{
    [Display("姓名")]
    [Required]
    public string Name { get; set; }

    [Display("班级")]
    [Required]
    public string Class { get; set; }

    [Display("地址")]
    [Required]
    public string Address { get; set; }

    [Display("邮箱")]
    [Required]
    public string Emall { get; set; }

    [Display("可用")]
    [Required]
    public bool IsEnbled { get; set; }

    [Display("时间")]
    [Required]
    public DateTime time { get; set; }

    [Display("年龄")]
    [Required]
    public int Age { get; set; }

    [Display("平均分")]
    public double Score { get; set; }

    [Display("电话号码")]
    [Required]
    [RegularExpression(@"^1[3|4|5|7|8][0-9]{9}$", ErrorMessage = "手机号码不合法!")]
    public string Tel { get; set; }
}
  • DisplayAttribute:表示名を指定するために使用
  • RequiredAttribute:データが null でないことを指定
  • RegularExpressionAttribute:正規表現によるデータ検証
  • その他の属性は随時更新...

適用方法:

<UserControl.Resources>
  <local:Student
    x:Key="S.Student.HeBianGu"
    Name="河边骨"
    Address="四川省成都市高新区"
    Class="四年级"
    Emall="7777777777@QQ.com"
    Age="33"
    Score="99.99"
    IsEnbled="True"
    time="2019-09-09"
  />
</UserControl.Resources>

<wpfcontrollib:ObjectPropertyForm
  Grid.Row="1"
  Title="学生信息"
  SelectObject="{StaticResource S.Student.HeBianGu}"
>
  <base:Interaction.Behaviors>
    <base:MouseDragElementBehavior ConstrainToParentBounds="True" />
    <base:SelectZIndexElementBehavior /> </base:Interaction.Behaviors
></wpfcontrollib:ObjectPropertyForm>

2.4 コード

2.4.1 リフレクションによるプロパティと属性の取得

ObservableCollection<ObjectPropertyItem> PropertyItemSource
{
    get { return (ObservableCollection<ObjectPropertyItem>)GetValue(PropertyItemSourceProperty); }
    set { SetValue(PropertyItemSourceProperty, value); }
}

// Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty PropertyItemSourceProperty =
    DependencyProperty.Register("PropertyItemSource", typeof(ObservableCollection<ObjectPropertyItem>), typeof(ObjectPropertyForm), new PropertyMetadata(new ObservableCollection<ObjectPropertyItem>(), (d, e) =>
      {
          ObjectPropertyForm control = d as ObjectPropertyForm;

          if (control == null) return;

          ObservableCollection<ObjectPropertyItem> config = e.NewValue as ObservableCollection<ObjectPropertyItem>;

      }));


void RefreshObject(object o)
{
    Type type = o.GetType();

    var propertys = type.GetProperties();

    this.PropertyItemSource.Clear();

    foreach (var item in propertys)
    {
        var from = ObjectPropertyFactory.Create(item, o);

        this.PropertyItemSource.Add(from);
    }

    this.ItemsSource = this.PropertyItemSource;
}

2.4.2 型基底クラス、拡張クラス、およびファクトリメソッドの定義

/// <summary> 型基底クラス </summary>
public class ObjectPropertyItem : NotifyPropertyChanged
{
    public string Name { get; set; }
    public PropertyInfo PropertyInfo { get; set; }

    public object Obj { get; set; }
    public ObjectPropertyItem(PropertyInfo property, object obj)
    {
        PropertyInfo = property;


        var display = property.GetCustomAttribute<DisplayAttribute>();

        Name = display == null ? property.Name : display.Name;

        Obj = obj;
    }


}

/// <summary> ジェネリック型基底クラス </summary>
public class ObjectPropertyItem<T> : ObjectPropertyItem
{
    private T _value;
    /// <summary> 説明 </summary>
    public T Value
    {
        get { return _value; }
        set
        {

            this.Message = null;

            //  Do:データの検証
            if (Validations != null)
            {
                foreach (var item in Validations)
                {
                    if (!item.IsValid(value))
                    {
                        this.Message = item.ErrorMessage;
                    }
                }
            }

            _value = value;

            RaisePropertyChanged("Value");

            this.SetValue(value);
        }
    }

    void SetValue(T value)
    {
        this.PropertyInfo.SetValue(Obj, value);
    }

    List<ValidationAttribute> Validations { get; }

    public ObjectPropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
        Value = (T)property.GetValue(obj);

        Validations = property.GetCustomAttributes<ValidationAttribute>()?.ToList();

        if(Validations!=null&& Validations.Count>0)
        {
            this.Flag = "*";
        }
    }



    private string _message;
    /// <summary> 説明 </summary>
    public string Message
    {
        get { return _message; }
        set
        {
            _message = value;
            RaisePropertyChanged("Message");
        }
    }

    public string Flag { get; set; }

}

/// <summary> 文字列プロパティ型 </summary>
public class StringPropertyItem : ObjectPropertyItem<string>
{
    public StringPropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
    }
}

/// <summary> 日時プロパティ型 </summary>
public class DateTimePropertyItem : ObjectPropertyItem<DateTime>
{
    public DateTimePropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
    }
}

/// <summary> Doubleプロパティ型 </summary>
public class DoublePropertyItem : ObjectPropertyItem<double>
{
    public DoublePropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
    }
}

/// <summary> Intプロパティ型 </summary>

public class IntPropertyItem : ObjectPropertyItem<int>
{
    public IntPropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
    }
}

/// <summary> Boolプロパティ型 </summary>
public class BoolPropertyItem : ObjectPropertyItem<bool>
{
    public BoolPropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
    }
}

型ファクトリ:

public class ObjectPropertyFactory
{
    public static ObjectPropertyItem Create(PropertyInfo info, object obj)
    {
        if (info.PropertyType == typeof(int))
        {
            return new IntPropertyItem(info, obj);
        }
        else if (info.PropertyType == typeof(string))
        {
            return new StringPropertyItem(info, obj);
        }
        else if (info.PropertyType == typeof(DateTime))
        {
            return new DateTimePropertyItem(info, obj);
        }
        else if (info.PropertyType == typeof(double))
        {
            return new DoublePropertyItem(info, obj);
        }
        else if (info.PropertyType == typeof(bool))
        {
            return new BoolPropertyItem(info, obj);
        }

        return null;
    }
}

2.4.3 スタイルテンプレート

<DataTemplate DataType="{x:Type base:StringPropertyItem}">
  <Grid
    Width="{Binding RelativeSource={RelativeSource AncestorType=local:ObjectPropertyForm},Path=Width-5}"
    Height="35"
    Margin="5,0"
  >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="2*" />
      <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>

    <TextBlock
      Text="{Binding Name}"
      FontSize="14"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="{Binding Flag}"
      Grid.Column="1"
      Margin="5,0"
      FontSize="14"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
    />

    <local:FTextBox
      Text="{Binding Value,UpdateSourceTrigger=PropertyChanged}"
      Style="{DynamicResource DefaultTextBox}"
      FontSize="14"
      Width="Auto"
      CaretBrush="Black"
      Grid.Column="2"
      Height="30"
      base:ControlAttachProperty.FIcon=""
      VerticalContentAlignment="Center"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="&#xe626;"
      Grid.Column="3"
      Style="{DynamicResource FIcon }"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      Visibility="{Binding Message,Converter={x:Static base:XConverter.VisibilityWithOutStringConverter},ConverterParameter={x:Null},Mode=TwoWay}"
      FontSize="14"
      TextTrimming="CharacterEllipsis"
      ToolTip="{Binding Message}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />
  </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type base:BoolPropertyItem}">
  <Grid
    Width="{Binding RelativeSource={RelativeSource AncestorType=local:ObjectPropertyForm},Path=Width-5}"
    Height="35"
    Margin="5,0"
  >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="2*" />
      <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>

    <TextBlock
      Text="{Binding Name}"
      FontSize="14"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="{Binding Flag}"
      Grid.Column="1"
      Margin="5,0"
      FontSize="14"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
    />
    <CheckBox
      IsChecked="{Binding Value}"
      FontSize="14"
      Grid.Column="2"
      Height="30"
      VerticalContentAlignment="Center"
      HorizontalAlignment="Left"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="&#xe626;"
      Grid.Column="3"
      Style="{DynamicResource FIcon }"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      Visibility="{Binding Message,Converter={x:Static base:XConverter.VisibilityWithOutStringConverter},ConverterParameter={x:Null}}"
      FontSize="14"
      TextTrimming="CharacterEllipsis"
      ToolTip="{Binding Message}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />
  </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type base:DateTimePropertyItem}">
  <Grid
    Width="{Binding RelativeSource={RelativeSource AncestorType=local:ObjectPropertyForm},Path=Width-5}"
    Height="35"
    Margin="5,0"
  >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="2*" />
      <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>

    <TextBlock
      Text="{Binding Name}"
      FontSize="14"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="{Binding Flag}"
      Grid.Column="1"
      Margin="5,0"
      FontSize="14"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
    />
    <DatePicker
      SelectedDate="{Binding Value}"
      FontSize="14"
      Grid.Column="2"
      Height="30"
      VerticalContentAlignment="Center"
      Width="Auto"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="&#xe626;"
      Grid.Column="3"
      Style="{DynamicResource FIcon }"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      Visibility="{Binding Message,Converter={x:Static base:XConverter.VisibilityWithOutStringConverter},ConverterParameter={x:Null}}"
      FontSize="14"
      TextTrimming="CharacterEllipsis"
      ToolTip="{Binding Message}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />
  </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type base:IntPropertyItem}">
  <Grid
    Width="{Binding RelativeSource={RelativeSource AncestorType=local:ObjectPropertyForm},Path=Width-5}"
    Height="35"
    Margin="5,0"
  >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="2*" />
      <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>

    <TextBlock
      Text="{Binding Name}"
      FontSize="14"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="{Binding Flag}"
      Grid.Column="1"
      Margin="5,0"
      FontSize="14"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
    />
    <Slider
      Value="{Binding Value}"
      FontSize="14"
      Grid.Column="2"
      Height="30"
      VerticalContentAlignment="Center"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="&#xe626;"
      Grid.Column="3"
      Style="{DynamicResource FIcon }"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      Visibility="{Binding Message,Converter={x:Static base:XConverter.VisibilityWithOutStringConverter},ConverterParameter={x:Null}}"
      FontSize="14"
      TextTrimming="CharacterEllipsis"
      ToolTip="{Binding Message}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />
  </Grid>
</DataTemplate>

<DataTemplate DataType="{x:Type base:DoublePropertyItem}">
  <Grid
    Width="{Binding RelativeSource={RelativeSource AncestorType=local:ObjectPropertyForm},Path=Width-5}"
    Height="35"
    Margin="5,0"
  >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="2*" />
      <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>

    <TextBlock
      Text="{Binding Name}"
      FontSize="14"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="{Binding Flag}"
      Grid.Column="1"
      Margin="5,0"
      FontSize="14"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
    />
    <Slider
      Value="{Binding Value}"
      FontSize="14"
      Grid.Column="2"
      Height="30"
      VerticalContentAlignment="Center"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="&#xe626;"
      Grid.Column="3"
      Style="{DynamicResource FIcon }"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      Visibility="{Binding Message,Converter={x:Static base:XConverter.VisibilityWithOutStringConverter},ConverterParameter={x:Null}}"
      FontSize="14"
      TextTrimming="CharacterEllipsis"
      ToolTip="{Binding Message}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />
  </Grid>
</DataTemplate>

<style TargetType="local:ObjectPropertyForm">
  <Setter Property="Background" Value="{DynamicResource S.Brush.TextBackgroud.Default}"/>
  <Setter Property="BorderThickness" Value="0"/>
  <!--<Setter Property="BorderBrush" Value="{x:Null}"/>-->
  <Setter Property="HorizontalAlignment" Value="Stretch"/>
  <Setter Property="VerticalAlignment" Value="Center"/>
  <Setter Property="HorizontalContentAlignment" Value="Center"/>
  <Setter Property="VerticalContentAlignment" Value="Center"/>
  <!--<Setter Property="FocusVisualStyle" Value="{x:Null}"/>-->
  <Setter Property="Padding" Value="0" />
  <Setter Property="Width" Value="500" />
  <Setter Property="Height" Value="Auto" />
  <Setter Property="ItemsSource" Value="{Binding PropertyItemSource,Mode=TwoWay}" />
  <Setter Property="ItemsPanel">
      <Setter.Value>
          <ItemsPanelTemplate>
              <StackPanel/>

          </ItemsPanelTemplate>
      </Setter.Value>
  </Setter>
  <Setter Property="Template">
      <Setter.Value>
          <ControlTemplate TargetType="local:ObjectPropertyForm">
              <GroupBox Header="{TemplateBinding Title}">
                  <Border HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
                      VerticalAlignment="{TemplateBinding VerticalAlignment}"
                      Background="{TemplateBinding Background}"
                      BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}">
                      <ItemsPresenter/>
                  </Border>
              </GroupBox>
          </ControlTemplate>
      </Setter.Value>
  </Setter>
</style>

2.4.4 拡張方法

2.4.4.1 拡張型を定義するだけです。例:
/// <summary> 文字列プロパティ型 </summary>
public class StringPropertyItem : ObjectPropertyItem<string>
{
    public StringPropertyItem(PropertyInfo property, object obj) : base(property, obj)
    {
    }
}
2.4.4.2 次に DataTemplate を追加します。例:
<DataTemplate DataType="{x:Type base:StringPropertyItem}">
  <Grid
    Width="{Binding RelativeSource={RelativeSource AncestorType=local:ObjectPropertyForm},Path=Width-5}"
    Height="35"
    Margin="5,0"
  >
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="2*" />
      <ColumnDefinition Width="30" />
    </Grid.ColumnDefinitions>

    <TextBlock
      Text="{Binding Name}"
      FontSize="14"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="{Binding Flag}"
      Grid.Column="1"
      Margin="5,0"
      FontSize="14"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      HorizontalAlignment="Right"
      VerticalAlignment="Center"
    />

    <local:FTextBox
      Text="{Binding Value,UpdateSourceTrigger=PropertyChanged}"
      Style="{DynamicResource DefaultTextBox}"
      FontSize="14"
      Width="Auto"
      CaretBrush="Black"
      Grid.Column="2"
      Height="30"
      base:ControlAttachProperty.FIcon=""
      VerticalContentAlignment="Center"
      HorizontalAlignment="Stretch"
      VerticalAlignment="Center"
    />

    <TextBlock
      Text="&#xe626;"
      Grid.Column="3"
      Style="{DynamicResource FIcon }"
      Foreground="{DynamicResource S.Brush.Red.Notice}"
      Visibility="{Binding Message,Converter={x:Static base:XConverter.VisibilityWithOutStringConverter},ConverterParameter={x:Null},Mode=TwoWay}"
      FontSize="14"
      TextTrimming="CharacterEllipsis"
      ToolTip="{Binding Message}"
      HorizontalAlignment="Center"
      VerticalAlignment="Center"
    />
  </Grid>
</DataTemplate>

3. メッセージダイアログ

原文タイトル:サンプル:WPF におけるカスタム MessageService による DialogHost、Snackbar、NotifyIcon を使った各種シナリオのメッセージ表示
原文リンク:https://blog.csdn.net/u010975589/article/details/95985190

3.1 目的

異なる対話シナリオでは異なるメッセージの表示が必要であり、異なるメッセージには異なる表示効果が必要です。DialogHost(ダイアログ)、NotifyIcon(通知アイコン)、Snackbar(吹き出しメッセージ)を用いてさまざまなシナリオのメッセージを表示し、ViewModel 内で使用します。

3.2 実装

  1. 待機ダイアログ
  2. OK ダイアログ
  3. OK/キャンセルダイアログ
  4. パーセンテージ進捗・テキスト進捗ダイアログ
  5. 吹き出し通知メッセージ(NotifyIcon)
  6. スナックバーメッセージ(Snackbar)

3.3 サンプル

説明:

  1. ダイアログ:通常のダイアログメッセージは上図の通り。待機ダイアログ、メッセージダイアログ、進捗ダイアログがあります(現時点ではこれらのみラッピングしています。カスタムダイアログはユーザーコントロールを作成し、汎用ロードメソッドを呼び出すだけです。随時更新...)

  2. スナックバーメッセージ:進捗保存が成功した場合などに、2 秒間表示して自動で非表示になるメッセージです(図中の「友情提示」部分)。

  3. 吹き出し通知メッセージ:プログラムが最小化状態などにある場合に、通知アイコンでメッセージを表示します。

3.4 コード

[ViewModel("Loyout")]
class LoyoutViewModel : MvcViewModelBase
{


    /// <summary> コマンド汎用メソッド </summary>
    protected override async void RelayMethod(object obj)

    {
        string command = obj?.ToString();

        //  Do:ダイアログメッセージ
        if (command == "Button.ShowDialogMessage")
        {
            await MessageService.ShowSumitMessge("这是消息对话框?");

        }
        //  Do:待機メッセージ
        else if (command == "Button.ShowWaittingMessge")
        {

            await MessageService.ShowWaittingMessge(() => Thread.Sleep(2000));

        }
        //  Do:パーセンテージ進捗ダイアログ
        else if (command == "Button.ShowPercentProgress")
        {
            Action<IPercentProgress> action = l =>
                {
                    for (int i = 0; i < 100; i++)
                    {
                        l.Value = i;

                        Thread.Sleep(50);
                    }

                    Thread.Sleep(1000);

                    MessageService.ShowSnackMessageWithNotice("加载完成!");
                };
            await MessageService.ShowPercentProgress(action);

        }
        //  Do:テキスト進捗ダイアログ
        else if (command == "Button.ShowStringProgress")
        {
            Action<IStringProgress> action = l =>
            {
                for (int i = 1; i <= 100; i++)
                {
                    l.MessageStr = $"正在提交当前页第{i}份数据,共100份";

                    Thread.Sleep(50);
                }

                Thread.Sleep(1000);

                MessageService.ShowSnackMessageWithNotice("提交完成:成功100条,失败0条!");
            };

            await MessageService.ShowStringProgress(action);

        }
        //  Do:確認/キャンセルダイアログ
        else if (command == "Button.ShowResultMessge")
        {
            Action<object, DialogClosingEventArgs> action = (l, k) =>
            {
                if ((bool)k.Parameter)
                {
                    MessageService.ShowSnackMessageWithNotice("你点击了取消");
                }
                else
                {
                    MessageService.ShowSnackMessageWithNotice("你点击了确定");
                }
            };

            MessageService.ShowResultMessge("确认要退出系统?", action);


        }
        //  Do:スナックバーメッセージ
        else if (command == "Button.ShowSnackMessage")
        {
            MessageService.ShowSnackMessageWithNotice("这是提示消息?");
        }
        //  Do:通知アイコンメッセージ
        else if (command == "Button.ShowNotifyMessage")
        {
            MessageService.ShowNotifyMessage("你有一条报警信息需要处理,请检查", "Notify By HeBianGu");
        }
    }
}

4. WPF で MVC を適用する

原文タイトル:ラッピング:WPF ベースの MVC フレームワークのカスタム開発の概要
原文リンク:https://blog.csdn.net/u010975589/article/details/100019431

4.1 目的

ASP.NET Core を使用する際、MVC フレームワークがページ遷移やデータ処理において非常に便利だと感じていました。しかし WPF には既存の MVC フレームワークがほとんどないため、カスタム MVC フレームワークを開発しました。その過程でフレームワークの利点も実感できました。以下、MVVM ベースの MVC フレームワークについて簡単に紹介します。

4.2 プロジェクト構成

主に 3 つの部分から構成されます:ControllerViewViewModel

View と ViewModel は従来の WPF における MVVM パターンです。

異なる点は、ページ遷移に Controller を適用したことです。以下に Controller の定義例を示します。

4.3 Controller の構造と定義

4.3.1 LoyoutController の定義

[Route("Loyout")]
class LoyoutController : Controller
{

    public LoyoutController(ShareViewModel shareViewModel) : base(shareViewModel)
    {

    }

    public async Task<IActionResult> Center()
    {
        return View();
    }

    [Route("OverView/Button")]
    public async Task<IActionResult> Mdi()
    {
        return View();
    }

    public async Task<IActionResult> Left()
    {
        return View();
    }

    public async Task<IActionResult> Right()
    {
        return View();
    }

    public async Task<IActionResult> Top()
    {
        return View();
    }

    public async Task<IActionResult> Bottom()
    {
        return View();
    }

    [Route("OverView/Toggle")]
    public async Task<IActionResult> Toggle()
    {
        return View();
    }

    [Route("OverView/Carouse")]
    public async Task<IActionResult> Carouse()
    {
        return View();
    }

    [Route("OverView/Evaluate")]
    public async Task<IActionResult> Evaluate()
    {
        return View();
    }

    [Route("OverView/Expander")]
    public async Task<IActionResult> Expander()
    {
        return View();
    }

    [Route("OverView/Gif")]
    public async Task<IActionResult> Gif()
    {
        return View();
    }

    [Route("OverView/Message")]
    public async Task<IActionResult> Message()
    {
        return View();
    }

    [Route("OverView/Upgrade")]
    public async Task<IActionResult> Upgrade()
    {
        return View();
    }

    [Route("OverView/Property")]
    public async Task<IActionResult> Property()
    {
        return View();
    }

    [Route("OverView/ProgressBar")]
    public async Task<IActionResult> ProgressBar()
    {
        return View();
    }

    [Route("OverView/Slider")]
    public async Task<IActionResult> Slider()
    {
        return View();
    }

    [Route("OverView/Tab")]
    public async Task<IActionResult> Tab()
    {
        return View();
    }

    [Route("OverView/Tree")]
    public async Task<IActionResult> Tree()
    {
        return View();
    }

    [Route("OverView/Observable")]
    public async Task<IActionResult> Observable()
    {
        return View();
    }

    [Route("OverView/Brush")]
    public async Task<IActionResult> Brush()
    {
        return View();
    }

    [Route("OverView/Shadow")]
    public async Task<IActionResult> Shadow()
    {
        return View();
    }

    [Route("OverView/Button")]
    public async Task<IActionResult> Button()
    {
        await MessageService.ShowWaittingMessge(() => Thread.Sleep(500));

        this.ViewModel.ButtonContentText = DateTime.Now.ToString();

        return View();

    }



    [Route("OverView/Grid")]
    public async Task<IActionResult> Grid()
    {
        return View();
    }

    [Route("OverView/Combobox")]
    public async Task<IActionResult> Combobox()
    {
        return View();
    }

    [Route("OverView")]
    public async Task<IActionResult> OverView()
    {
        await MessageService.ShowWaittingMessge(() => Thread.Sleep(500));

        MessageService.ShowSnackMessageWithNotice("OverView");

        return View();
    }

    [Route("OverView/TextBox")]
    public async Task<IActionResult> TextBox()
    {
        return View();
    }

    [Route("OverView/Book")]
    public async Task<IActionResult> Book()
    {
        return View();
    }

    [Route("OverView/Xaml")]
    public async Task<IActionResult> Xaml()
    {
        return View();
    }

    [Route("OverView/Dimension")]
    public async Task<IActionResult> Dimension()
    {
        return View();
    }

    [Route("OverView/Geometry")]
    public async Task<IActionResult> Geometry()
    {
        return View();
    }

    [Route("OverView/Panel")]
    public async Task<IActionResult> Panel()
    {
        return View();
    }
    [Route("OverView/Transform3D")]
    public async Task<IActionResult> Transform3D()
    {
        return View();
    }

    [Route("OverView/Drawer")]
    public async Task<IActionResult> Drawer()
    {
        return View();
    }
}

4.3.2 フロントエンドページ

以下の図において、赤色の部分は Controller 内の遷移先 Route に対応します。

例:赤色の Button を選択すると、まず Button() メソッドが呼び出され、現在の Controller に対応する View フォルダ内の ButtonControl.xaml ページに遷移します。

[Route("OverView/Button")]
public async Task<IActionResult> Button()
{
    await MessageService.ShowWaittingMessge(() => Thread.Sleep(500);

    this.ViewModel.ButtonContentText = DateTime.Now.ToString();

    return View();

}

Button() メソッド内では、現在の ViewModel に対する CRUD 操作などのビジネスロジックを記述できます。Controller のメンバーである ViewModel は、内部でカプセル化された ViewModel で、ViewModel フォルダ内の現在の Controller に対応する ViewModel です。

4.3.3 サンプル

4.3.4 左側の Xaml リストは以下のように定義できます。

<Grid>
  <wpfcontrollib:LinkGroupExpander
    ScrollViewer.HorizontalScrollBarVisibility="Disabled"
    x:Name="selectloyout"
    SelectedLink="{Binding SelectLink,Mode=TwoWay}"
    Command="{x:Static wpfcontrollib:DrawerHost.CloseDrawerCommand}"
    CommandParameter="{x:Static Dock.Left}"
  >
    <wpfcontrollib:LinkActionGroup DisplayName="基础控件" Logo="&#xe69f;">
      <wpfcontrollib:LinkActionGroup.Links>
        <wpfcontrollib:LinkAction
          DisplayName="Button"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Button"
        />
        <wpfcontrollib:LinkAction
          DisplayName="TextBox"
          Logo="&#xe6a3;"
          Controller="Loyout"
          Action="TextBox"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Combobox"
          Logo="&#xe6a3;"
          Controller="Loyout"
          Action="Combobox"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Toggle"
          Logo="&#xe6a3;"
          Controller="Loyout"
          Action="Toggle"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Evaluate"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Evaluate"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Expander"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Expander"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Gif"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Gif"
        />
        <wpfcontrollib:LinkAction
          DisplayName="ProgressBar"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="ProgressBar"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Slider"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Slider"
        />
      </wpfcontrollib:LinkActionGroup.Links>
    </wpfcontrollib:LinkActionGroup>

    <wpfcontrollib:LinkActionGroup DisplayName="布局控件" Logo="&#xe69f;">
      <wpfcontrollib:LinkActionGroup.Links>
        <wpfcontrollib:LinkAction
          DisplayName="MdiControl"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Mdi"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Carouse"
          Logo="&#xe69e;"
          Controller="Loyout"
          Action="Carouse"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Tab"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Tab"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Tree"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Tree"
        />
        <wpfcontrollib:LinkAction
          DisplayName="ObservableSource"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Observable"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Property"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Property"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Panel"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Panel"
        />
      </wpfcontrollib:LinkActionGroup.Links>
    </wpfcontrollib:LinkActionGroup>

    <wpfcontrollib:LinkActionGroup DisplayName="全局控件" Logo="&#xe69f;">
      <wpfcontrollib:LinkActionGroup.Links>
        <wpfcontrollib:LinkAction
          DisplayName="Message"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Message"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Upgrade"
          Logo="&#xe69e;"
          Controller="Loyout"
          Action="Upgrade"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Drawer"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Drawer"
        />
      </wpfcontrollib:LinkActionGroup.Links>
    </wpfcontrollib:LinkActionGroup>

    <wpfcontrollib:LinkActionGroup DisplayName="全局样式" Logo="&#xe69f;">
      <wpfcontrollib:LinkActionGroup.Links>
        <wpfcontrollib:LinkAction
          DisplayName="Brush"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Brush"
        />
        <wpfcontrollib:LinkAction
          DisplayName="Shadow"
          Logo="&#xe69f;"
          Controller="Loyout"
          Action="Shadow"
        />
      </wpfcontrollib:LinkActionGroup.Links>
    </wpfcontrollib:LinkActionGroup>
  </wpfcontrollib:LinkGroupExpander>
</Grid>

LinkGroupExpander コントロールを使用し、LinkAction をラッピングしてページ遷移を実現します。LinkAction のいくつかのプロパティを定義するだけで、指定ページに遷移できます。例えば:

  • Controller プロパティ:遷移先の Controller を指定
  • Action プロパティ:遷移先のメソッドを指定
  • DisplayName プロパティ:UI に表示する名前
  • Logo プロパティ:UI に表示するアイコン

以下の例では、Controller 内の Button() メソッドに対応する遷移設定を示します。

[Route("OverView/Button")]
public async Task<IActionResult> Button()
<wpfcontrollib:LinkAction
  DisplayName="Button"
  Logo="&#xe69f;"
  Controller="Loyout"
  Action="Button"
/>

4.3.5 Controller 基底クラス ControllerBase の定義

主要メソッドは IActionResult View([CallerMemberName] string name = "") です。このメソッドは MVC 実装の中核であり、リフレクションによりアセンブリを動的にロードし、プロジェクト構成の View、ViewModel をロードして IActionResult を生成し、メインページに返してページ遷移を行います。コードは以下の通りです。

public abstract class ControllerBase : IController
{
    protected virtual IActionResult View([CallerMemberName] string name = "")
    {
        var route = this.GetType().GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();

        string controlName = null;

        if (route.FirstOrDefault() == null)
        {
            controlName = this.GetType().Name;
        }
        else
        {
            controlName = route.FirstOrDefault().Name;
        }

        var ass = Assembly.GetEntryAssembly().GetName();

        string path = $"/{ass.Name};component/View/{controlName}/{name}Control.xaml";

        Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);

        var content = Application.Current.Dispatcher.Invoke(() =>
        {
            return Application.LoadComponent(uri);
        });

        ActionResult result = new ActionResult();

        result.Uri = uri;
        result.View = content as ContentControl;

        Type type = Assembly.GetEntryAssembly().GetTypeOfMatch<NotifyPropertyChanged>(l => l.Name == controlName + "ViewModel");

        result.ViewModel = ServiceRegistry.Instance.GetInstance(type);

        Application.Current.Dispatcher.Invoke(() =>
        {
            (result.View as FrameworkElement).DataContext = result.ViewModel;

        });

        return result;
    }


    protected virtual IActionResult LinkAction([CallerMemberName] string name = "")
    {
        var route = this.GetType().GetCustomAttributes(typeof(RouteAttribute), true).Cast<RouteAttribute>();

        string controlName = null;

        if (route.FirstOrDefault() == null)
        {
            controlName = this.GetType().Name;
        }
        else
        {
            controlName = route.FirstOrDefault().Name;
        }

        var ass = Assembly.GetEntryAssembly().GetName();

        string path = $"/{ass.Name};component/View/{controlName}/{name}Control.xaml";

        Uri uri = new Uri(path, UriKind.RelativeOrAbsolute);

        var content = Application.Current.Dispatcher.Invoke(() =>
        {
            return Application.LoadComponent(uri);
        });

        ActionResult result = new ActionResult();

        result.Uri = uri;
        result.View = content;

        Type type = Assembly.GetEntryAssembly().GetTypeOfMatch<NotifyPropertyChanged>(l => l.Name == controlName + "ViewModel");

        result.ViewModel = ServiceRegistry.Instance.GetInstance(type);

        Application.Current.Dispatcher.Invoke(() =>
        {
            (result.View as FrameworkElement).DataContext = result.ViewModel;
        });

        return result;
    }

}

説明:

  1. Application.LoadComponent(uri); により Control をロードして生成します。
  2. ViewModel 基底クラス NotifyPropertyChanged をリフレクションで特定し、対応する ViewModel を見つけて View にバインドします。
  3. View と ViewModel を IActionResult にラップしてメインページに返し、ロードします。

Controller 内のメソッドの戻り値型は async Task<IActionResult> であり、ページ遷移全体が非同期で実行されるため、ページ切り替え時のフリーズを効果的に防止できます。

4.4 View の構造と定義

View はプロジェクト内で Controller のメソッドに対応して定義されます。MVC では、構造を厳密に [View/Loyout] のように定義する必要があります。これによりコード量が減り、統一されたフォーマットで整理されます。構造は以下の通りです。

赤色の ButtonControl.xaml は Controller 内の Button() メソッドが遷移するページです。他のページも同様です。

4.5 ViewModel の構造と定義

LoyoutViewModelLoyoutController と View/Loyout 以下のすべてのページに対応する ViewModel です。

4.6 MVC 構成全体の効果

以上が WPF における MVC 適用の簡単なサンプルです。詳細な内容やサンプルは以下のリンクからコードをダウンロードして確認してください。

コードアドレス:https://github.com/HeBianGu/WPF-ControlBase.git

Sqlite データベースを適用した別のサンプルは以下の通りです。

コードアドレス:https://github.com/HeBianGu/WPF-ExplorerManager.git

5. その他機能の説明

原文タイトル:サンプル:カスタム WPF ベース UI コントロールライブラリ HeBianGu.General.WpfControlLib V2.0
原文リンク:https://blog.csdn.net/u010975589/article/details/103083605

5.1 目的

よく使うコントロールをカスタムコントロールライブラリにラッピングし、迅速な開発を支援します。

5.2 実装機能

  • 基本的なよく使うコントロールを実装し、一般的なソフトウェアの迅速な開発に対応
  • .NET Core 3.0+ および .NET Framework 4.5+ をサポート

5.3 全体概要

5.3.1 ログインページ

ログインページは LoginWindowBase 基底クラスを継承し、スタイル Style="{StaticResource S.Window.Login.Default}" を設定するだけで実現できます。

5.3.2 メインページ

メインページは LinkWindowBase 基底クラスを継承し、スタイル Style="{DynamicResource S.Window.Link.Default}" を設定するだけで実現できます。

メインウィンドウ全体は ViewBox 方式でロードされ、ウィンドウの拡大縮小や他の解像度デバイスにも対応します。

5.3.3 テーマ設定情報の保存

テーマ設定情報は ApplicationBase にラッピングされており、終了時に設定されたテーマ情報(テーマカラー、フォントサイズなど)を自動保存します。

まとめ: このパターンを適用することで、共通部分を基底にラッピングして再利用できます。スタイルを変更する場合は、Style ファイルまたは依存関係プロパティを変更するだけで機能変更に対応できます。

5.4 テーマ設定

明るいテーマの例:

暗いテーマの例:

テーマ設定機能は主に以下のものを含みます。

  1. テーマのメインカラー設定

テーマカラーは強調表示する部分を指定するために使用します。内蔵色の選択、システムテーマカラーへの追従、カスタム色の選択、動的テーマ(指定時間ごとに自動変更)が可能です。

  1. テーマ設定

現在実装されているテーマは、明るいテーマ、暗いテーマ、グレーテーマ、メインカラーテーマの 4 種類です。

  1. フォントサイズ設定

フォントサイズは Large と Small の 2 種類が組み込まれており、これらは注入方式で読み込まれます。プログラムロード時に初期値を設定できます。

  1. その他の設定

言語(中・英)、標準行の高さなど、プログラムロード時に初期設定可能です。詳細は割愛します。

まとめ: このように設計する理由は、見た目の好みは人それぞれであり、カスタム設定を用いることで多様なニーズに可能な限り対応できるからです。

5.5 その他基本コントロール

5.5.1 データテーブル

  • a テーマのフォントとテーマ設定に対応。以降のすべてのコントロールでも同様にテーマ設定が適用されます(説明省略)。
  • b 1 ページあたりの表示行数

1 ページに表示する行数を設定できます。

  • c 検索

検索フィルタ条件を設定し、条件に一致する項目のみ表示します。

  • d ページ遷移

前のページ、次のページ、最初のページ、最後のページ、指定ページへの移動が可能です。

  • e ページ情報

現在のページがデータソースの何番目から何番目までか、データソースの総件数を表示します。

  • f 2 種類のグリッドページスタイル

まとめ: これらの機能は PagedDataGrid コントロールにラッピングされており、データソースをバインドするだけで上記機能を実現できます。印刷やエクスポート機能は未実装です。

5.5.2 ツリーリスト

  • a カテゴリによるフィルタリングに対応

上図のように、指定タイプを選択してリストをフィルタリングします。

  • b 条件による検索に対応

上図のように、条件を入力してリストをフィルタリングします。

まとめ: 使用方法は、TreeListView コントロールにデータソースをバインドするだけです。

5.5.3 その他よく使うコントロール

  • a ダイアログ

内蔵ダイアログを使用しており、ウィンドウではなくオーバーレイレイヤーとして実装されているため、ウィンドウダイアログに起因する問題を回避できます。

  • b カスタムダイアログウィンドウ

システムダイアログより美しく、表示・非表示のエフェクトを追加。注入方式でボタンの数や機能をカスタマイズできます。

  • c メッセージリスト

現在 2 つのモードがあります。ウィンドウ内に表示するモードと、Windows システムに表示するモードです。ニーズに応じて表示方法をカスタマイズできます。例:

  • d オンラインアップデートの例:

  • e ナビゲーションメニューの例:

  • f その他の機能には以下を含みます。

ボタンコントロール、テキスト入力コントロール、ドロップダウンリストコントロール、数値コントロール、日付選択コントロール、バインディング対応のパスワードボックスコントロール、プログレスバーコントロール、ドラッグコントロール、ツリーコントロール、ページングコントロール、その他のカスタムコントロール。

上記のコントロールはすべて、テーマカラーやフォントサイズの切り替えに対応しており、一般的なソフトウェアの機能要件を満たせます。

全体の構造はカスタム MVC 方式でロードされます。参考アドレス:https://blog.csdn.net/u010975589/article/details/100019431

コントロールが多数あるため詳細は割愛します。興味があればソースコードをダウンロードするか、nuget パッケージを追加してください。

5.6 使用方法

nuget パッケージの追加は以下の図を参照してください。

説明:このサンプルの一部機能はサードパーティのフレームワークを参考にしており、オープンソースは学習および参考目的のみで、商用目的ではありません。

このフレームワークを適用した他のサンプル:

5.7 ダウンロードアドレス

GitHub ダウンロードアドレス:GitHub - HeBianGu/WPF-ControlBase: Wpf ラッピングのカスタムコントロールリソースライブラリ

インストールパッケージのサンプルダウンロードアドレス:

更新:2019.12.16 .NET Core 3.0 対応追加

現在 Core3.0 と .net 4.5 をサポートしています。ソリューションのアセンブリが読み込めない場合は、これらのフレームワークをインストールしてください。

さらに探索

関連読書

その他の記事
同じカテゴリ / 同じタグ 2025/09/13

WPF から Avalonia への移行シリーズ:なぜ WPF プログラムを Avalonia に移行しなければならないのか

過去数年間、当社の上位機ソフトウェアは主に WPF と WinForm で開発されてきました。これらの技術は Windows プラットフォームで非常に便利であり、小規模試作から現在の規模拡大による納品まで、私たちを支えてきました。しかし、ビジネスの発展や顧客ニーズの変化に伴い、単一の Windows テクノロジースタックは私たちが必ず乗り越えなければならない壁となってきました。

続きを読む
同じカテゴリ / 同じタグ 2025/01/26

WPF カスタムXMLファイルによる国際化

この記事では、WPFプログラムでカスタムXMLファイルを使用して国際化を実現する方法について詳しく説明します。必要なNuGetパッケージのインストール、言語リストの動的取得、言語の動的切り替え、コードおよびXAMLインターフェースでの翻訳文字列の使用などを含み、ソースコードのリンクも提供し、開発者がWPFアプリケーションの国際化を簡単に実装できるように支援します。

続きを読む