WPF 3D shuttle effect, use WPF to join in the fun

WPF 3D shuttle effect, use WPF to join in the fun

Compared with CSS and UWP, the old WPF is relatively cumbersome to operate 3D effects. You need to first make a 3D model, then rotate the XY axis, then adjust the Z-axis stretch, and finally adjust the FOV. The shuttle effect depends on the number stretched on the Z-axis

最后更新 2/21/2022 1:58 PM
ARM830
预计阅读 14 分钟
分类
WPF
标签
.NET WPF

CSS effects blog address:

3D 穿梭效果?使用 CSS 轻松搞定 - ChokCoco - 博客园 (cnblogs.com)

UWP effects blog address

3D 穿梭效果?使用 UWP 也能搞定 - dino.c - 博客园 (cnblogs.com)

The big guys are really at the top of the world.

You can really learn a lot.

I just joined in and tried using WPF. The effect was not very good, so I could barely get by.

Compared with CSS and UWP, the old WPF is relatively cumbersome to operate 3D effects. You need to first make a 3D model, then rotate the XY axis, then adjust the Z-axis stretch, and finally adjust the FOV. The shuttle effect depends on the number stretched on the Z-axis, which is 10 - 20 in the figure.

Of course, the most difficult thing is the connection between the end and the end of the animation.

Let's do it step by step.

Making 3D models

There is actually nothing to study whether this piece can be directly used or not, so it is a 3D model directly and the picture exists in the form of a texture.

As for 3D model making, to sum up, it starts from left to right.

  1. Positions

3D models are composed of triangles, so when describing what an object looks like, draw triangles as much as possible. Every point is a vertex. If there is the same vertex, just choose one.

When describing a vertex, it is XYZ. When you look at the object head-on, the direction the object's Z-axis points towards you is the positive direction. The Y axis is positive upward, and the X axis is positive rightward.

It is worth noting here that for convenience, it is recommended to use the order of left, left, bottom, right, bottom and right to define points.

The vertex definitions of this model are P1 (x, y, z), P2 (x, y, z), P3 (x, y, z), P4 (x, y, z)

  1. TriangleIndices

This describes how these vertices form a triangle. When we think of a face as a front, remember to define the vertex set subscripts counterclockwise. The set of vertices is Positions, and the content is the coordinate points you enter, with the subscripts starting from 0.

The back is clockwise.

  1. TextureCoordinates

This is the positioning order of the materials, and this compares the 2D coordinate system and is different from the others. That is, the upper left corner is 0, the lower right corner of 0 is 1, 1, which is the ordinary screen coordinate system

To make a good model, you need to make four.

    • Rotating 3D model **

This is more interesting. Four models have been made above. We need to form four faces, that is, rotate the Y axis and the X axis, but the directions are different. To rotate, we can use Transform.

Looks okay.

Let's make four pictures and try them out.

Convenient debugging, we bring progress bars.

resources

<MeshGeometry3D x:Key="Rect3D_O">
  <MeshGeometry3D.Positions>
    -50, 50, 0, -50, -50, 0, 50, -50, 0, 50, 50, 0
  </MeshGeometry3D.Positions>
  <MeshGeometry3D.TriangleIndices> 0 1 2 2 3 0 </MeshGeometry3D.TriangleIndices>
  <MeshGeometry3D.TextureCoordinates>
    0,0 0,1 1,1 1,0
  </MeshGeometry3D.TextureCoordinates>
  <MeshGeometry3D.Normals> 0,0,1, 0,0,1, 0,0,1, 0,0,1 </MeshGeometry3D.Normals>
</MeshGeometry3D>
<DiffuseMaterial x:Key="Img">
  <DiffuseMaterial.Brush>
    <ImageBrush ImageSource="start.jpg" />
  </DiffuseMaterial.Brush>
</DiffuseMaterial>
<Viewport3D x:Name="View3D_2">
  <Viewport3D.Camera>
    <PerspectiveCamera
      FieldOfView="{Binding ElementName=FOV, Path=Value}"
      Position="0,0,100"
      LookDirection="0,0,-1"
    />
  </Viewport3D.Camera>
  <ModelVisual3D>
    <ModelVisual3D.Content>
      <Model3DGroup>
        <AmbientLight Color="White" />
        <GeometryModel3D
          Geometry="{StaticResource Rect3D_O}"
          Material="{StaticResource Img}"
          BackMaterial="{StaticResource Img}"
        >
          <GeometryModel3D.Transform>
            <Transform3DGroup>
              <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D
                    Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
                    Axis="0 1 0"
                  />
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
              <ScaleTransform3D
                ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
              />
            </Transform3DGroup>
          </GeometryModel3D.Transform>
        </GeometryModel3D>
        <GeometryModel3D
          Geometry="{StaticResource Rect3D_O}"
          Material="{StaticResource Img}"
          BackMaterial="{StaticResource Img}"
        >
          <GeometryModel3D.Transform>
            <Transform3DGroup>
              <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D
                    Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
                    Axis="1 0 0"
                  />
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
              <ScaleTransform3D
                ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
              />
            </Transform3DGroup>
          </GeometryModel3D.Transform>
        </GeometryModel3D>
        <GeometryModel3D
          Geometry="{StaticResource Rect3D_O}"
          Material="{StaticResource Img}"
          BackMaterial="{StaticResource Img}"
        >
          <GeometryModel3D.Transform>
            <Transform3DGroup>
              <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D
                    Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
                    Axis="0 1 0"
                  />
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
              <ScaleTransform3D
                ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
              />
            </Transform3DGroup>
          </GeometryModel3D.Transform>
        </GeometryModel3D>
        <GeometryModel3D
          Geometry="{StaticResource Rect3D_O}"
          Material="{StaticResource Img}"
          BackMaterial="{StaticResource Img}"
        >
          <GeometryModel3D.Transform>
            <Transform3DGroup>
              <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                <RotateTransform3D.Rotation>
                  <AxisAngleRotation3D
                    Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
                    Axis="1 0 0"
                  />
                </RotateTransform3D.Rotation>
              </RotateTransform3D>
              <ScaleTransform3D
                ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
              />
            </Transform3DGroup>
          </GeometryModel3D.Transform>
        </GeometryModel3D>
      </Model3DGroup>
    </ModelVisual3D.Content>
  </ModelVisual3D>
</Viewport3D>

<Slider
  Grid.Row="1"
  x:Name="Left_Top_Z"
  Minimum="-90"
  Value="12"
  Maximum="90"
  ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
  Grid.Row="2"
  x:Name="Right_Bottom_Z"
  Minimum="-90"
  Value="-12"
  Maximum="0"
  ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>
<Slider
  Grid.Row="3"
  x:Name="ScaleZ_2"
  Background="Red"
  Minimum="10"
  Maximum="100"
  ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
>
  <Slider.Style>
    <style TargetType="Slider">
      <Style.Triggers>
          <Trigger Property="Tag" Value="1">
              <Trigger.EnterActions>
                  <BeginStoryboard >
                      <Storyboard>
                          <DoubleAnimation  RepeatBehavior="Forever"  Storyboard.TargetProperty="Value"   From="11" To="20" BeginTime="0:0:0"   Duration="0:0:10" />
                      </Storyboard>
                  </BeginStoryboard>
              </Trigger.EnterActions>
          </Trigger>
      </Style.Triggers>
    </style>
  </Slider.Style>
</Slider>
<Slider
  Grid.Row="4"
  x:Name="FOV"
  Minimum="0"
  Maximum="180"
  Value="176"
  ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
/>

Run and see

It looks like it has a taste.

The rest is to debug the animation.

We want animations that are connected from end to end, but in fact, we want the two animations to not see the difference between the beginning and the end. Referring to the basic ideas of the big shots, relying on transparency and delaying playback makes it impossible to see the difference between the beginning and the end.

that is, to

Here, we implement this solution purely in XAML.

First of all, we must solve the timing problem. First, when the page loads, we modify a certain value of an object through animation. There is a trigger on this object. Monitor the object and start timing, and then play the animation.

To modify the value, we need to use ObjectAnimationUsingKeyFrames animation.

Triggers, numerical triggers, because they can be bound.

In the whole process, the most important thing is the timeline, when the two sets of animations appear and when they are hidden.

    • Final Code **
<Window.Resources>
  <Storyboard
    RepeatBehavior="Forever"
    Storyboard.TargetName="ScaleZ_2"
    Duration="0:0:10"
    x:Key="sb"
  >
    <DoubleAnimation
      Storyboard.TargetName="ScaleZ_2"
      Storyboard.TargetProperty="Value"
      From="11"
      To="20"
      BeginTime="0:0:0"
      Duration="0:0:10"
    />
    <DoubleAnimationUsingKeyFrames
      Storyboard.TargetProperty="Opacity"
      Storyboard.TargetName="View3D_2"
      BeginTime="0:0:0"
    >
      <LinearDoubleKeyFrame KeyTime="0:0:0" Value="0" />
      <LinearDoubleKeyFrame KeyTime="0:0:2" Value="1" />
      <LinearDoubleKeyFrame KeyTime="0:0:6" Value="1" />
      <LinearDoubleKeyFrame KeyTime="0:0:10" Value="0" />
    </DoubleAnimationUsingKeyFrames>
  </Storyboard>
  <MeshGeometry3D x:Key="Rect3D_O">
    <MeshGeometry3D.Positions>
      -50, 50, 0, -50, -50, 0, 50, -50, 0, 50, 50, 0
    </MeshGeometry3D.Positions>
    <MeshGeometry3D.TriangleIndices>
      0 1 2 2 3 0
    </MeshGeometry3D.TriangleIndices>
    <MeshGeometry3D.TextureCoordinates>
      0,0 0,1 1,1 1,0
    </MeshGeometry3D.TextureCoordinates>
    <MeshGeometry3D.Normals>
      0,0,1, 0,0,1, 0,0,1, 0,0,1
    </MeshGeometry3D.Normals>
  </MeshGeometry3D>
  <DiffuseMaterial x:Key="Img">
    <DiffuseMaterial.Brush>
      <ImageBrush ImageSource="start.jpg" />
    </DiffuseMaterial.Brush>
  </DiffuseMaterial>
</Window.Resources>
<Window.Triggers>
  <EventTrigger RoutedEvent="Loaded" SourceName="ScaleZ">
    <BeginStoryboard>
      <Storyboard
        Storyboard.TargetName="ScaleZ"
        Storyboard.TargetProperty="Tag"
        Duration="0:0:5.1"
      >
        <ObjectAnimationUsingKeyFrames>
          <DiscreteObjectKeyFrame KeyTime="0:0:5">
            <DiscreteObjectKeyFrame.Value> 1 </DiscreteObjectKeyFrame.Value>
          </DiscreteObjectKeyFrame>
        </ObjectAnimationUsingKeyFrames>
      </Storyboard>
    </BeginStoryboard>
    <BeginStoryboard Storyboard="{StaticResource sb}" />
  </EventTrigger>
</Window.Triggers>
<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
    <RowDefinition Height="auto" />
  </Grid.RowDefinitions>
  <Viewport3D x:Name="View3D_1">
    <Viewport3D.Style>
      <style TargetType="Viewport3D">
        <Style.Triggers>
            <DataTrigger Binding="{Binding ElementName=ScaleZ,Path=Value}" Value="1">
                <DataTrigger.EnterActions>
                    <BeginStoryboard >
                        <Storyboard>
                            <DoubleAnimationUsingKeyFrames  RepeatBehavior="Forever" Storyboard.TargetProperty="Opacity"  BeginTime="0:0:0"  Duration="0:0:10">
                                <LinearDoubleKeyFrame KeyTime="0:0:0" Value="0"/>
                                <LinearDoubleKeyFrame KeyTime="0:0:5" Value="1"/>
                                <LinearDoubleKeyFrame KeyTime="0:0:8" Value="1"/>
                                <LinearDoubleKeyFrame KeyTime="0:0:10" Value="0"/>
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </DataTrigger.EnterActions>
            </DataTrigger>
        </Style.Triggers>
      </style>
    </Viewport3D.Style>
    <Viewport3D.Camera>
      <PerspectiveCamera
        FieldOfView="{Binding ElementName=FOV, Path=Value}"
        Position="0,0,100"
        LookDirection="0,0,-1"
      />
    </Viewport3D.Camera>
    <ModelVisual3D>
      <ModelVisual3D.Content>
        <Model3DGroup>
          <AmbientLight Color="White" />
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
                      Axis="0 1 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
                      Axis="1 0 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
                      Axis="0 1 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
                      Axis="1 0 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
        </Model3DGroup>
      </ModelVisual3D.Content>
    </ModelVisual3D>
  </Viewport3D>
  <Viewport3D x:Name="View3D_2">
    <Viewport3D.Camera>
      <PerspectiveCamera
        FieldOfView="{Binding ElementName=FOV, Path=Value}"
        Position="0,0,100"
        LookDirection="0,0,-1"
      />
    </Viewport3D.Camera>
    <ModelVisual3D>
      <ModelVisual3D.Content>
        <Model3DGroup>
          <AmbientLight Color="White" />
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
                      Axis="0 1 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Left_Top_Z, Path=Value}"
                      Axis="1 0 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
                      Axis="0 1 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
          <GeometryModel3D
            Geometry="{StaticResource Rect3D_O}"
            Material="{StaticResource Img}"
            BackMaterial="{StaticResource Img}"
          >
            <GeometryModel3D.Transform>
              <Transform3DGroup>
                <RotateTransform3D CenterX="0" CenterY="1" CenterZ="0">
                  <RotateTransform3D.Rotation>
                    <AxisAngleRotation3D
                      Angle="{Binding ElementName=Right_Bottom_Z, Path=Value}"
                      Axis="1 0 0"
                    />
                  </RotateTransform3D.Rotation>
                </RotateTransform3D>
                <ScaleTransform3D
                  ScaleZ="{Binding  ElementName=ScaleZ_2, Path=Value}"
                />
              </Transform3DGroup>
            </GeometryModel3D.Transform>
          </GeometryModel3D>
        </Model3DGroup>
      </ModelVisual3D.Content>
    </ModelVisual3D>
  </Viewport3D>

  <Slider
    Grid.Row="1"
    x:Name="Left_Top_Z"
    Minimum="-90"
    Value="12"
    Maximum="90"
    ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
  />
  <Slider
    Grid.Row="2"
    x:Name="Right_Bottom_Z"
    Minimum="-90"
    Value="-12"
    Maximum="0"
    ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
  />
  <Slider
    Grid.Row="3"
    x:Name="ScaleZ"
    Background="Red"
    Minimum="10"
    Maximum="100"
    ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
  >
    <Slider.Style>
      <style TargetType="Slider">
        <Style.Triggers>
            <Trigger Property="Tag" Value="1">
                <Trigger.EnterActions>
                    <BeginStoryboard >
                        <Storyboard>
                            <DoubleAnimation  RepeatBehavior="Forever"  Storyboard.TargetProperty="Value"   From="11" To="20" BeginTime="0:0:0"   Duration="0:0:10" />
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
            </Trigger>
        </Style.Triggers>
      </style>
    </Slider.Style>
  </Slider>
  <Slider
    Grid.Row="4"
    x:Name="FOV"
    Minimum="0"
    Maximum="180"
    Value="176"
    ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
  />
  <Slider
    Grid.Row="5"
    x:Name="ScaleZ_2"
    Background="Black"
    Minimum="10"
    Maximum="100"
    ToolTip="{Binding  RelativeSource={RelativeSource Mode=Self}, Path=Value}"
  />
  <StackPanel Grid.Row="6" Orientation="Vertical">
    <TextBlock>
      <Run Text=" View3D_1 透明度" />
      <Run Text="{Binding ElementName=View3D_1, Path=Opacity}" />
      <Run Text=" View3D_1 value" />
      <Run Text="{Binding ElementName=ScaleZ, Path=Value}" />
      <Run Text=" Tag value" />
      <Run Text="{Binding ElementName=ScaleZ, Path=Tag}" />
      <Run Text=" Zindex value" />
      <Run Text="{Binding ElementName=View3D_1, Path=(Panel.ZIndex)}" />
    </TextBlock>
    <TextBlock>
      <Run Text=" View3D_2 透明度" />
      <Run Text="{Binding ElementName=View3D_2, Path=Opacity}" />
      <Run Text=" View3D_2 value" />
      <Run Text="{Binding ElementName=ScaleZ_2, Path=Value}" />
      <Run Text=" Zindex value" />
      <Run Text="{Binding ElementName=View3D_2, Path=(Panel.ZIndex)}" />
    </TextBlock>
  </StackPanel>
</Grid>

In this way, we don't need any C#code to achieve this effect.

I must admit that I didn't do very well in the first 0 seconds, with a slight double image, so there is still a lot to learn.

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.

继续阅读