动态加载控件,采用UserControl在WrapPanel控件中动态加载,并且实现随着窗体变化实现自适应换行。
效果如下:
Window.xaml
<Window x:Class="testMultiDevices.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:testMultiDevices"
Title="MainWindow" Width="Auto" Height="Auto">
<Grid>
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding UserControls}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" x:Name="wrapPanel"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="15"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</Window>
MainWindow.cs
using System.Windows;
using System.Windows.Controls;
namespace testMultiDevices
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ItemsControl itemsControl = FindName("itemsControl") as ItemsControl;
if (itemsControl != null)
{
MainViewModel viewModel = new MainViewModel(itemsControl);
DataContext = viewModel;
this.SizeChanged += (sender, e) =>
{
viewModel.WindowSizeChangedCommand.Execute(e.NewSize);
};
}
//this.MinHeight = 500;
//this.MinWidth = 500;
}
}
}
MainViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.Windows.Controls;
using System.Windows.Input;
using System.ComponentModel;
using System.Windows;
namespace testMultiDevices
{
public class MainViewModel :Notify
{
private readonly ItemsControl itemsControl;
public ObservableCollection<UserControl> UserControls { get; set; }
private double availableWidth;
private double availableHeight;
public double AvailableWidth
{
get { return availableWidth; }
set
{
if (availableWidth != value)
{
availableWidth = value;
OnPropertyChanged();
UpdateItemWidth();
}
}
}
public double AvailableHeight
{
get { return availableHeight; }
set
{
if (availableHeight != value)
{
availableHeight = value;
OnPropertyChanged();
UpdateItemHeight();
}
}
}
public ICommand WindowSizeChangedCommand { get; set; }
public MainViewModel(ItemsControl itemsControl)
{
this.itemsControl = itemsControl;
UserControls = new ObservableCollection<UserControl>();
for (int i = 0; i < 10; i++)
{
AddUserControl();
}
WindowSizeChangedCommand = new MyCommand(ExecuteWindowSizeChanged);
}
public void AddUserControl()
{
var control = new testControl();
UserControls.Add(control);
}
private void ExecuteWindowSizeChanged(object parameter)
{
if (parameter != null && parameter.GetType() == typeof(Size))
{
Size size = (Size)parameter;
AvailableWidth = size.Width;
AvailableHeight = size.Height;
}
}
private void UpdateItemWidth()
{
if (UserControls.Count > 0 && itemsControl != null)
{
double gapWidth = 15;
double effectiveWidthForCount = itemsControl.ActualWidth - itemsControl.Margin.Left - itemsControl.Margin.Right;
int itemCountPerRow = (int)((effectiveWidthForCount + gapWidth) / (150 + gapWidth));
if (itemCountPerRow == 0) itemCountPerRow = 1;
double newItemWidth = (effectiveWidthForCount - (itemCountPerRow - 1) * gapWidth) / itemCountPerRow;
foreach (var item in UserControls)
{
UserControl userControl = item as UserControl;
if (userControl != null)
{
userControl.Width = newItemWidth;
}
}
}
}
private void UpdateItemHeight()
{
if (UserControls.Count > 0 && itemsControl != null)
{
double gapHeight = 10; // 假设垂直间隙为10,可调整
double effectiveHeight = itemsControl.ActualHeight - itemsControl.Margin.Top - itemsControl.Margin.Bottom;
double itemHeight = 100; // 假设用户控件高度为100,可根据实际调整
int rowCount = (int)((effectiveHeight + gapHeight) / (itemHeight + gapHeight));
if (rowCount == 0) rowCount = 1;
double newItemHeight = (effectiveHeight - (rowCount - 1) * gapHeight) / rowCount;
foreach (var item in UserControls)
{
UserControl userControl = item as UserControl;
if (userControl != null)
{
userControl.Height = newItemHeight;
}
}
}
}
}
}
testControl.xaml
<UserControl x:Class="testMultiDevices.testControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:testMultiDevices"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="150">
<Grid Margin="15,15,15,15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Background="BurlyWood" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" Grid.RowSpan="3"/>
<Border Background="Blue" Grid.Row="2" Grid.ColumnSpan="3" Grid.RowSpan="3"/>
<Grid Grid.Column="0" Grid.Row="1">
<Border Background="Aqua"/>
<Label Content="测试" HorizontalAlignment="Right" VerticalContentAlignment="Center"/>
</Grid>
<Grid Grid.Column="1" Grid.Row="1">
<Border Background="Beige"/>
<Label Content="工艺异常" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"/>
</Grid>
</Grid>
</UserControl>
MyCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace testMultiDevices
{
public class MyCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
private readonly Action<object> execute2;
private readonly Predicate<object> canExecute2;
// 无参数命令的构造函数
public MyCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
// 带参数命令的构造函数
public MyCommand(Action<object> execute, Predicate<object> canExecute = null)
{
execute2 = execute ?? throw new ArgumentNullException(nameof(execute));
canExecute2 = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
if (execute2 != null)
{
return canExecute2 == null || canExecute2(parameter);
}
return _canExecute == null || _canExecute();
}
public void Execute(object parameter)
{
if (execute2 != null)
{
execute2(parameter);
}
else
{
_execute();
}
}
}
}
Notify.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace testMultiDevices
{
public abstract class Notify : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}