WPF open source project: WPF-ControlBase

WPF open source project: WPF-ControlBase

Warehouse README is very simple, but you will like it after reading several blog posts posted by the author README

最后更新 11/30/2021 4:57 PM
He BianGu
预计阅读 35 分钟
分类
WPF
专题
WPF control library WPF Open Source Project
标签
.NET WPF WPF Open Source Project open source projects open source

仓库截图

Warehouse README is very simple, but you will like it after reading several blog posts posted by the author README. Without further ado, please visit the introduction directory:

  1. animation encapsulation
https://blog.csdn.net/u010975589/article/details/95974854
  1. property sheet
https://blog.csdn.net/u010975589/article/details/95970200
  1. message conversation
https://blog.csdn.net/u010975589/article/details/95985190
  1. Applying MVC in WPF
https://blog.csdn.net/u010975589/article/details/100019431
  1. Other functional descriptions
https://blog.csdn.net/u010975589/article/details/103083605

The following is a detailed description:

1. animation encapsulation

原文标题:示例:WPF中自定义StoryBoarService在代码中封装StoryBoard、Animation用于简化动画编写
原文链接:https://blog.csdn.net/u010975589/article/details/95974854

1.1 Purpose: Simplify animation writing by encapsulating StoryBoard and Animation

1.2 example

Description: Fading hidden is a commonly used animation in WPF. The picture above shows the effect after being encapsulated by StoryBoarService. You only need to execute the following code in the code:

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

The above closing effect can define a command as follows:

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;
}

Call the following command in Xaml to complete the effect of fading concealment

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

The passed CommandParmeter will gradually disappear when executing the command

Among them, the animation effect code only needs one sentence of code, which simplifies the cumbersome encoding process of animation in the code.

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

1.3 Code:

Currently, only DoubleAnimation encapsulation is implemented, and other types will be encapsulated in the future

1.3.1 Closing and modifying base classes

/// <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 Open expansion of 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 Excessive effect factories

/// <summary> 说明:https://docs.microsoft.com/zh-cn/dotnet/framework/wpf/graphics-multimedia/easing-functions </summary>
public static class EasingFunctionFactroy
{
    /// <summary> PowerEase:创建加速和/或减速使用的公式的动画f(t) = tp其中 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) = t2。 </summary>
    public static QuadraticEase QuadraticEase { get; set; } = new QuadraticEase();

    /// <summary> CubicEase:创建加速和/或减速使用的公式的动画f(t) = t3。 </summary>
    public static CubicEase CubicEase { get; set; } = new CubicEase();
    /// <summary> QuarticEase:创建加速和/或减速使用的公式的动画f(t) = t4。 </summary>
    public static QuarticEase QuarticEase { get; set; } = new QuarticEase();
    /// <summary> QuinticEase:创建加速和/或减速使用的公式的动画f(t) = t5。 </summary>
    public static QuinticEase QuinticEase { get; set; } = new QuinticEase();

    /// <summary> ExponentialEase:创建加速和/或减速使用指数公式的动画。 </summary>
    public static ExponentialEase ExponentialEase { get; set; } = new ExponentialEase();

    /// <summary> SineEase:创建加速和/或减速使用正弦公式的动画。 </summary>
    public static SineEase SineEase { get; set; } = new SineEase();

}

1.3.4 Usage method

/// <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. property sheet

原文标题:示例:WPF开发的简单ObjectProperyForm用来绑定实体表单
原文链接:https://blog.csdn.net/u010975589/article/details/95970200

2.1 Purpose: Custom controls to directly bind entity data and simplify the development cycle

2.2 achieve

  1. Binding entity objects
  2. Display attribute names by attribute
  3. Add verification conditions through features
  4. Several simple types of DataTemplate templates such as String, Int, Double, DateTime, and Bool have been implemented, and other templates support extensions.
  5. Other subsequent updates...

2.3 example

Entities are defined as follows:

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: Used to identify the display name
  • ResuiredAttribute: Used to identify that data cannot be empty
  • RigularExpression: Reference regular expressions to verify whether the data matches
  • Subsequent updates to other features...

Application method:

<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 code

2.4.1 Obtaining attributes and characteristics through reflection

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 Defining type base classes, extension classes, and factory methods

/// <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)
    {
    }
}

Type Factory:

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 Style templates

<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 Open extensions

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 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. message conversation

原文标题:示例:WPF中自定义MessageService应用DialogHost、Snackbar、NotifyIcon显示各种场景提示消息
原文链接:https://blog.csdn.net/u010975589/article/details/95985190

3.1 objective

Different interactive scenarios need to prompt different messages, and different messages need to be displayed with different effects. Use DialogHost (dialog box), NotifyIcon (message prompt), and Snackbar (bubble message) to display various scene prompt messages, which are applied in ViewModel

3.2 achieve

  1. Waiting dialog box
  2. OK dialog box
  3. OK and Cancel dialog box
  4. Percent Progress and Text Progress dialog boxes
  5. Bubble alert message (NotifyIcon)
  6. Prompt message (Snackbar)

3.3 example

Description:

  1. Dialog box: Regular dialogue messages are as shown in the figure above, waiting for dialogue box, message dialogue, and progress dialogue box;

(Currently, only the above are encapsulated. To customize the dialog box, you only need to create user controls and call the common loading method. Subsequent updates...)

  1. Prompt message: When the progress is saved successfully, a prompt message is needed, and it can be automatically hidden in 2s (as shown in the friendly prompt part in the picture);

  2. Bubble message: When the program is hidden or in a certain state, a bubble prompt message needs to be applied;

3.4 code

[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. Applying MVC in WPF

原文标题:封装:简要介绍自定义开发基于WPF的MVC框架
原文链接:https://blog.csdn.net/u010975589/article/details/100019431

4.1 objective

When using ASP.NET Core, I deeply felt that the MVC framework was convenient for processing page jump data. However, there seemed to be no ready-made MVC framework in WPF, so I customized and developed a set of MVC frameworks. I also realized the advantages of the framework during use. Here is a brief introduction to this set of MVC frameworks based on MVVM

4.2 project structure

主要有三部分组成:ControllerViewViewModel

Among them, View and ViewModel are the MVVM models in traditional WPF

The difference is that page jumps are applied to the Controller for control. The following example is the definition of Controller

4.3 Structure and definition of controller

4.3.1 Defining 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 Front-end pages

As follows, the red part corresponds to the Route to be jumped in the Controller

如:选择了红色部分的 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();

}

You can write some business logic in the Button () method, such as general operations such as adding, deleting, modifying and checking the current ViewModel. The current Controller member ViewModel is an internally encapsulated ViewModel, which corresponds to the ViewModel of the current Controller under the ViewModel file.

4.3.3 Example

4.3.4 The Xaml list on the left can be defined as follows

<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>

Through the LinkGroupExpander control, LinkAction is encapsulated to realize page jumps. Only a few properties of LinkAction are defined to achieve the effect of jumping to the specified page, such as:

  • Controller attribute: Used to indicate which Controller to jump to
  • Action attribute: Used to indicate which method to jump to
  • DisplayName property: The name displayed in the UI
  • Logo property: The icon displayed in the UI

As follows, the jump configuration corresponding to the Button () method in Controller is as follows

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

4.3.5 Definition of Controller Base Class 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;
    }

}

Description:

  1. Load the generation control through Application.LoadComponent (uri);
  2. Find the corresponding ViewModel by reflecting the ViewModel base class NotifyPropertyChanged and bind it to the View
  3. Encapsulate View and ViewModel into IActionResult and return them to the main page for loading

The return type of the method in the Controller is async Task, which means that the entire page jump is performed asynchronously, which can effectively avoid the stuck effect during page switching.

4.4 Structure and definitions in View

The definition of View in the project is based on the method correspondence in the Controller. In MVC, the structure definition [View/Loyout] must be strictly followed. The advantage is that it can reduce the amount of code and make the format uniform and the code neat. The structure is as follows:

The red ButtonControl.xaml is the page to be jumped by the Button () method in the Controller, and the same applies to other pages

4.5 Structure and definition of ViewModel

The LoyoutViewModel is the ViewModel corresponding to the LoyoutController and all pages under the entire View/Loyout

4.6 The overall MVC structure achieves the following effects

The above is a brief example of MVC application in WPF. The specific content and examples can be viewed by downloading the code from the following link

Code address: https://www.example.com

Another example of applying Sqlite database is as follows

Code address: https://www.example.com

5. Other functional descriptions

原文标题:示例:自定义WPF底层控件UI库 HeBianGu.General.WpfControlLib V2.0版本
原文链接:https://blog.csdn.net/u010975589/article/details/103083605

5.1 objective

Encapsulates some controls into a custom control library for rapid development

5.2 achieve functional

  • Basically realize commonly used basic controls to meet the rapid development of regular software
  • Also supports frameworks. NET Core 3.0 +,. Net FrameWork 4.5 +

5.3 overall situation

5.3.1 Login Page

The login page only needs to inherit the LoginWindowBase base class and set the style =""

5.3.2 Main page

The main page only needs to inherit the LinkWindowBase base class and set the style =""

The entire main window is loaded in ViewBox mode, and it will be compatible when zooming the window or applying it to other resolution devices.

5.3.3 Saving theme configuration information

Theme configuration information has been encapsulated in ApplicationBase, and the set configuration information (such as theme color, font size, etc.) will be automatically saved when exiting

    • Summary: ** Applying this pattern can achieve the purpose of reuse, encapsulating the common part to the bottom layer. If you need to modify the style, you only need to modify the Style file or modify the dependent attributes to meet the functional modification.

5.4 theme settings

Examples of light-colored themes are as follows:

An example of a dark theme is as follows:

Theme setting functions mainly include:

  1. Set the main theme color

The theme color is mainly used to identify the part to be highlighted. Currently, you can select built-in color, you can choose to follow the system theme color, you can customize the color, and you can use dynamic themes (that is, set the theme to change automatically at a specified time)

  1. set a theme

The theme currently implements four themes, namely light theme, dark theme, gray theme, and main color theme

  1. Set font size

There are currently two built-in font sizes, Large and Small. These two colors are loaded by injection, which means that the initial values of the two fonts can be set when the program loads

  1. other configurations

Including Chinese and English, setting the standard line height, etc., you can initialize the settings when the program is loaded. I won't introduce it too much here

    • Summary: ** The purpose of this design is that the aesthetics vary from person to person, and the use of custom configuration can meet as many changing needs as possible

5.5 Other basic controls

5.5.1 Data tables

  • a Compatible with theme fonts and theme settings. Theme settings have been applied to all controls to be mentioned later, so I will not explain further
  • b Number of items displayed per page

You can set the number of items to be displayed per page

  • c search

You can set search filters so that entries containing the specified search terms are displayed

  • d page jump

You can use previous page, next page, first page, last page, specified page

  • e-page information

Which entry to which data source does the current page belong to, and the total number of entries in the data source

  • f Two styles of grid pages
    • Summary: ** The above functions are encapsulated in the control PagedDataGrid. The above functions can be realized by binding the data source. Among them, printing, export and other functions have not been implemented for the time being.

5.5.2 Tree list

  • a Support filtering by category

As shown above, select the specified type to filter the list

  • b Support conditional search

As shown in the figure above, you can filter the specified conditions by entering conditions

    • Summary: ** Usage method is to bind data sources to TreeListView control

5.5.3 Other commonly used controls

  • a dialog box

Using built-in dialog boxes, not application windows, just overlays, can avoid some problems caused by window dialog boxes

  • b Conversation Window Customize Conversation Window

Compared with the system dialogue window, it is more beautiful, with increased display and hiding effects, and the number of buttons and functions can be customized through injection.

  • c Message List

Currently, there are two modes, namely display in a window and display in the Window System. The display method can be customized according to your needs. The examples are as follows

  • d An example of online upgrade is as follows

  • e An example of a navigation menu is as follows

  • f Other functions include

Button controls, text input box controls, drop-down list controls, number controls, date selection controls, password box controls that support binding, progress bar controls, drag controls, tree controls, paging controls, and other custom controls.

The above controls have implemented theme colors, font sizes, etc., which can meet the functions of commonly used software.

The overall structure is loaded using a custom Mvc method. Reference address: https://www.example.com

由于控件过多不做详细介绍,有兴趣的可以下载源码或加载nuget包

5.6 use

Add nuget package as shown below

Description: Part of the function and part of the code of this example refer to third-party frameworks. Open source is only used for learning and reference, and is not for commercial purposes.

Other examples of applying this framework:

5.7 download address

GitHub 下载地址:GitHub - HeBianGu/WPF-ControlBase: Wpf 封装的自定义控件资源库

Installation package example download address:

Update: 2019.12.16 Added. NET Core 3.0

Core3.0 and.net 4.5 are currently supported. If there is a solution assembly that cannot be loaded, please install these two frameworks

Keep Exploring

延伸阅读

更多文章
同分类 / 同标签 9/13/2025

Migration from WPF to Avalonia series: Why I have to migrate WPF programs to Avalonia

In the past few years, our host computer software has been mainly developed using WPF and WinForm. These technologies are really easy to use on the Windows platform, and they have also accompanied us through the stage of small-scale trial production to today's large-scale delivery. However, with the development of business and changes in customer needs, the single Windows technology stack has gradually become a hurdle that we must overcome.

继续阅读
同分类 / 同标签 1/26/2025

WPF internationalizes with custom XML files

This article describes in detail the methods of using custom XML files to achieve internationalization in WPF programs, including installing the necessary NuGet package, dynamically obtaining language lists, dynamically switching languages, using translation strings in code and xaml interfaces, etc. It also provides source code links to help developers easily internationalize WPF applications.

继续阅读