mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-19 18:47:55 +08:00
perf: 干员识别与仓库识别支持虚拟化,大幅提高首次加载速度 (#16486)
https://github.com/user-attachments/assets/3a0722fb-b4c0-4864-9141-7d0200c117b8 ## Summary by Sourcery 优化仓库和运营商卡片列表的展示方式,支持基于行的虚拟化以及共享的卡片宽度约束,从而实现更快的初始加载和更一致的布局。 新功能: - 为虚拟化卡片列表添加共享卡片宽度行为,根据可用空间和列数保持一致的卡片宽度。 增强内容: - 将仓库和运营商集合重构为基于行的可观察集合,并计算列数,以更好地支持虚拟化网格布局。 - 连接集合变更处理程序,以在仓库和运营商数据更新时保持行展示和列数的同步。 <details> <summary>Original summary in English</summary> ## Summary by Sourcery Optimize depot and operator card list presentation to support row-based virtualization and shared card width constraints for faster initial loading and more consistent layouts. New Features: - Add a shared card width behavior for virtualized card lists to maintain consistent widths based on available space and column count. Enhancements: - Refactor depot and operator collections into row-based observable collections with computed column counts to better support virtualized grid layouts. - Wire collection change handlers to keep row presentations and column counts in sync when depot and operator data updates. </details>
This commit is contained in:
575
src/MaaWpfGui/Extensions/UIBehaviors/SharedCardWidthBehavior.cs
Normal file
575
src/MaaWpfGui/Extensions/UIBehaviors/SharedCardWidthBehavior.cs
Normal file
@@ -0,0 +1,575 @@
|
||||
// <copyright file="SharedCardWidthBehavior.cs" company="MaaAssistantArknights">
|
||||
// Part of the MaaWpfGui project, maintained by the MaaAssistantArknights team (Maa Team)
|
||||
// Copyright (C) 2021-2025 MaaAssistantArknights Contributors
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License v3.0 only as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY
|
||||
// </copyright>
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
|
||||
namespace MaaWpfGui.Extensions.UIBehaviors;
|
||||
|
||||
/// <summary>
|
||||
/// 为按行虚拟化的卡片列表维护统一的共享宽度,并根据父容器可用宽度动态约束单卡上限。
|
||||
/// </summary>
|
||||
public static class SharedCardWidthBehavior
|
||||
{
|
||||
#region ColumnCount
|
||||
|
||||
/// <summary>
|
||||
/// 指定共享宽度作用域当前使用的列数,用于按父容器宽度推导单卡的最大允许宽度。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty ColumnCountProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"ColumnCount",
|
||||
typeof(int),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(1, OnScopeLayoutParameterChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 设置共享宽度作用域的列数。
|
||||
/// </summary>
|
||||
/// <param name="obj">承载共享宽度作用域的元素。</param>
|
||||
/// <param name="value">当前布局的列数。</param>
|
||||
public static void SetColumnCount(DependencyObject obj, int value) => obj.SetValue(ColumnCountProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取共享宽度作用域的列数。
|
||||
/// </summary>
|
||||
/// <param name="obj">承载共享宽度作用域的元素。</param>
|
||||
/// <returns>当前布局的列数。</returns>
|
||||
public static int GetColumnCount(DependencyObject obj) => (int)obj.GetValue(ColumnCountProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region WidthPadding
|
||||
|
||||
/// <summary>
|
||||
/// 指定单卡宽度计算时需要预留的水平占位,用来抵消外边距、边框等额外宽度。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty WidthPaddingProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"WidthPadding",
|
||||
typeof(double),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(10d, OnScopeLayoutParameterChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 设置单卡宽度计算时的水平预留值。
|
||||
/// </summary>
|
||||
/// <param name="obj">承载共享宽度作用域的元素。</param>
|
||||
/// <param name="value">需要从每列可用宽度中扣除的水平占位。</param>
|
||||
public static void SetWidthPadding(DependencyObject obj, double value) => obj.SetValue(WidthPaddingProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取单卡宽度计算时的水平预留值。
|
||||
/// </summary>
|
||||
/// <param name="obj">承载共享宽度作用域的元素。</param>
|
||||
/// <returns>每列需要扣除的水平占位。</returns>
|
||||
public static double GetWidthPadding(DependencyObject obj) => (double)obj.GetValue(WidthPaddingProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region EnableScope
|
||||
|
||||
/// <summary>
|
||||
/// 标记某个 ItemsControl 是否作为共享宽度作用域的根节点。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty EnableScopeProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"EnableScope",
|
||||
typeof(bool),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(false, OnEnableScopeChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 设置元素是否启用共享宽度作用域。
|
||||
/// </summary>
|
||||
/// <param name="obj">候选作用域根节点。</param>
|
||||
/// <param name="value">为 true 时启用共享宽度管理。</param>
|
||||
public static void SetEnableScope(DependencyObject obj, bool value) => obj.SetValue(EnableScopeProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取元素是否启用了共享宽度作用域。
|
||||
/// </summary>
|
||||
/// <param name="obj">候选作用域根节点。</param>
|
||||
/// <returns>为 true 表示该元素负责维护整组卡片的共享宽度。</returns>
|
||||
public static bool GetEnableScope(DependencyObject obj) => (bool)obj.GetValue(EnableScopeProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region TrackWidth
|
||||
|
||||
/// <summary>
|
||||
/// 标记某个卡片元素是否参与共享宽度测量。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty TrackWidthProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"TrackWidth",
|
||||
typeof(bool),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(false, OnTrackWidthChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 设置元素是否参与共享宽度跟踪。
|
||||
/// </summary>
|
||||
/// <param name="obj">需要参与测量的卡片元素。</param>
|
||||
/// <param name="value">为 true 时会把该元素的实际宽度纳入共享宽度计算。</param>
|
||||
public static void SetTrackWidth(DependencyObject obj, bool value) => obj.SetValue(TrackWidthProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取元素是否参与共享宽度跟踪。
|
||||
/// </summary>
|
||||
/// <param name="obj">需要参与测量的卡片元素。</param>
|
||||
/// <returns>为 true 表示该元素会推动共享宽度增大。</returns>
|
||||
public static bool GetTrackWidth(DependencyObject obj) => (bool)obj.GetValue(TrackWidthProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region SharedMinWidth
|
||||
|
||||
/// <summary>
|
||||
/// 保存当前作用域内所有卡片共享的最小宽度,供每个卡片绑定统一值。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty SharedMinWidthProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"SharedMinWidth",
|
||||
typeof(double),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(0d));
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前作用域的共享最小宽度。
|
||||
/// </summary>
|
||||
/// <param name="obj">共享宽度作用域根节点。</param>
|
||||
/// <param name="value">所有卡片共同使用的最小宽度。</param>
|
||||
public static void SetSharedMinWidth(DependencyObject obj, double value) => obj.SetValue(SharedMinWidthProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前作用域的共享最小宽度。
|
||||
/// </summary>
|
||||
/// <param name="obj">共享宽度作用域根节点。</param>
|
||||
/// <returns>当前缓存的统一卡片宽度。</returns>
|
||||
public static double GetSharedMinWidth(DependencyObject obj) => (double)obj.GetValue(SharedMinWidthProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region InitialWidth
|
||||
|
||||
/// <summary>
|
||||
/// 指定作用域初始化或整体重算时使用的基准宽度,避免首次布局时过窄。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty InitialWidthProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"InitialWidth",
|
||||
typeof(double),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(0d));
|
||||
|
||||
/// <summary>
|
||||
/// 设置共享宽度的初始基准值。
|
||||
/// </summary>
|
||||
/// <param name="obj">共享宽度作用域根节点。</param>
|
||||
/// <param name="value">整体重算前先采用的基准宽度。</param>
|
||||
public static void SetInitialWidth(DependencyObject obj, double value) => obj.SetValue(InitialWidthProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取共享宽度的初始基准值。
|
||||
/// </summary>
|
||||
/// <param name="obj">共享宽度作用域根节点。</param>
|
||||
/// <returns>初始化或重算时使用的基准宽度。</returns>
|
||||
public static double GetInitialWidth(DependencyObject obj) => (double)obj.GetValue(InitialWidthProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
#region WidthLimit
|
||||
|
||||
/// <summary>
|
||||
/// 保存当前作用域内单个卡片允许使用的硬上限宽度。
|
||||
/// </summary>
|
||||
public static readonly DependencyProperty WidthLimitProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"WidthLimit",
|
||||
typeof(double),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(double.PositiveInfinity, OnWidthLimitChanged));
|
||||
|
||||
/// <summary>
|
||||
/// 设置当前作用域内单卡的宽度上限。
|
||||
/// </summary>
|
||||
/// <param name="obj">共享宽度作用域根节点。</param>
|
||||
/// <param name="value">单卡允许使用的最大宽度。</param>
|
||||
public static void SetWidthLimit(DependencyObject obj, double value) => obj.SetValue(WidthLimitProperty, value);
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前作用域内单卡的宽度上限。
|
||||
/// </summary>
|
||||
/// <param name="obj">共享宽度作用域根节点。</param>
|
||||
/// <returns>单卡允许使用的最大宽度。</returns>
|
||||
public static double GetWidthLimit(DependencyObject obj) => (double)obj.GetValue(WidthLimitProperty);
|
||||
|
||||
#endregion
|
||||
|
||||
// 监听 ItemsSource 变更,确保整表数据切换后重新扫描一次共享宽度。
|
||||
private static readonly DependencyPropertyDescriptor ItemsSourceDescriptor =
|
||||
DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
|
||||
|
||||
// 标记当前作用域是否已经排队等待重算,避免同一帧重复投递刷新请求。
|
||||
private static readonly DependencyProperty IsRecalculationQueuedProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"IsRecalculationQueued",
|
||||
typeof(bool),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
// 标记当前作用域是否正在执行整体重算,避免测量过程中的回调再次干扰共享宽度。
|
||||
private static readonly DependencyProperty IsRecalculatingProperty =
|
||||
DependencyProperty.RegisterAttached(
|
||||
"IsRecalculating",
|
||||
typeof(bool),
|
||||
typeof(SharedCardWidthBehavior),
|
||||
new PropertyMetadata(false));
|
||||
|
||||
private static void OnScopeLayoutParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ItemsControl itemsControl && GetEnableScope(itemsControl))
|
||||
{
|
||||
// 列数或间距变化后,需要重新按父容器宽度计算单卡上限。
|
||||
QueueScopeRecalculation(itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnWidthLimitChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is ItemsControl itemsControl)
|
||||
{
|
||||
CoerceSharedMinWidth(itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnEnableScopeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not ItemsControl itemsControl)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)e.NewValue)
|
||||
{
|
||||
itemsControl.Loaded += OnScopeLoaded;
|
||||
itemsControl.SizeChanged += OnScopeSizeChanged;
|
||||
ItemsSourceDescriptor?.AddValueChanged(itemsControl, OnScopeItemsSourceChanged);
|
||||
QueueScopeRecalculation(itemsControl);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemsControl.Loaded -= OnScopeLoaded;
|
||||
itemsControl.SizeChanged -= OnScopeSizeChanged;
|
||||
ItemsSourceDescriptor?.RemoveValueChanged(itemsControl, OnScopeItemsSourceChanged);
|
||||
ResetScope(itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTrackWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is not FrameworkElement element)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)e.NewValue)
|
||||
{
|
||||
element.Loaded += OnTrackedElementLoaded;
|
||||
element.SizeChanged += OnTrackedElementSizeChanged;
|
||||
UpdateSharedMinWidth(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
element.Loaded -= OnTrackedElementLoaded;
|
||||
element.SizeChanged -= OnTrackedElementSizeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnScopeLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ItemsControl itemsControl)
|
||||
{
|
||||
QueueScopeRecalculation(itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnScopeItemsSourceChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is ItemsControl itemsControl)
|
||||
{
|
||||
QueueScopeRecalculation(itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnScopeSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (!e.WidthChanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender is ItemsControl itemsControl)
|
||||
{
|
||||
// 共享宽度只在父容器宽度变化时允许收缩,卡片自身变化只允许把它撑大。
|
||||
QueueScopeRecalculation(itemsControl);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTrackedElementLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
UpdateSharedMinWidth(element);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnTrackedElementSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
UpdateSharedMinWidth(element);
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdateSharedMinWidth(FrameworkElement element)
|
||||
{
|
||||
var trackedWidth = GetTrackedWidth(element);
|
||||
if (trackedWidth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scope = FindScope(element);
|
||||
if (scope == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((bool)scope.GetValue(IsRecalculatingProperty))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 运行时上推共享宽度时,仍然必须先服从当前的单卡硬上限。
|
||||
trackedWidth = ClampWidth(scope, trackedWidth);
|
||||
if (trackedWidth <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var currentWidth = GetSharedMinWidth(scope);
|
||||
if (trackedWidth > currentWidth + 0.5)
|
||||
{
|
||||
SetSharedMinWidth(scope, trackedWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private static void ResetScope(ItemsControl itemsControl)
|
||||
{
|
||||
SetSharedMinWidth(itemsControl, ClampWidth(itemsControl, GetInitialWidth(itemsControl)));
|
||||
}
|
||||
|
||||
private static void QueueScopeRecalculation(ItemsControl itemsControl)
|
||||
{
|
||||
if ((bool)itemsControl.GetValue(IsRecalculationQueuedProperty) || itemsControl.Dispatcher.HasShutdownStarted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
itemsControl.SetValue(IsRecalculationQueuedProperty, true);
|
||||
|
||||
_ = itemsControl.Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() =>
|
||||
{
|
||||
itemsControl.SetValue(IsRecalculationQueuedProperty, false);
|
||||
|
||||
if (!GetEnableScope(itemsControl))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RecalculateScopeWidth(itemsControl);
|
||||
}));
|
||||
}
|
||||
|
||||
private static void RecalculateScopeWidth(ItemsControl itemsControl)
|
||||
{
|
||||
itemsControl.SetValue(IsRecalculatingProperty, true);
|
||||
|
||||
try
|
||||
{
|
||||
// 布局前后各刷新一次上限,确保依赖可视区的值都是最新的。
|
||||
UpdateWidthLimit(itemsControl);
|
||||
ResetScope(itemsControl);
|
||||
itemsControl.UpdateLayout();
|
||||
UpdateWidthLimit(itemsControl);
|
||||
|
||||
var maxWidth = GetInitialWidth(itemsControl);
|
||||
var widthLimit = GetWidthLimit(itemsControl);
|
||||
foreach (var trackedElement in EnumerateTrackedElements(itemsControl))
|
||||
{
|
||||
var trackedWidth = GetTrackedWidth(trackedElement);
|
||||
if (trackedWidth > maxWidth + 0.5)
|
||||
{
|
||||
maxWidth = trackedWidth;
|
||||
}
|
||||
}
|
||||
|
||||
SetSharedMinWidth(itemsControl, ClampWidth(itemsControl, maxWidth));
|
||||
}
|
||||
finally
|
||||
{
|
||||
itemsControl.SetValue(IsRecalculatingProperty, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemsControl? FindScope(DependencyObject? current)
|
||||
{
|
||||
while (current != null)
|
||||
{
|
||||
if (current is ItemsControl itemsControl && GetEnableScope(itemsControl))
|
||||
{
|
||||
return itemsControl;
|
||||
}
|
||||
|
||||
current = VisualTreeHelper.GetParent(current);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void UpdateWidthLimit(ItemsControl itemsControl)
|
||||
{
|
||||
var availableWidth = GetAvailableWidth(itemsControl);
|
||||
var columnCount = Math.Max(1, GetColumnCount(itemsControl));
|
||||
var widthPadding = GetWidthPadding(itemsControl);
|
||||
|
||||
if (availableWidth <= 0)
|
||||
{
|
||||
SetWidthLimit(itemsControl, double.PositiveInfinity);
|
||||
return;
|
||||
}
|
||||
|
||||
var widthLimit = Math.Max(0, (availableWidth / columnCount) - widthPadding);
|
||||
SetWidthLimit(itemsControl, widthLimit);
|
||||
}
|
||||
|
||||
private static void CoerceSharedMinWidth(ItemsControl itemsControl)
|
||||
{
|
||||
// SharedMinWidth 是缓存值,所以硬上限变化后要再做一次钳制。
|
||||
var currentWidth = GetSharedMinWidth(itemsControl);
|
||||
var clampedWidth = ClampWidth(itemsControl, currentWidth);
|
||||
if (Math.Abs(clampedWidth - currentWidth) > 0.5)
|
||||
{
|
||||
SetSharedMinWidth(itemsControl, clampedWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetAvailableWidth(ItemsControl itemsControl)
|
||||
{
|
||||
var scrollViewer = FindDescendant<ScrollViewer>(itemsControl);
|
||||
if (scrollViewer != null)
|
||||
{
|
||||
// 出现滚动条时,ViewportWidth 比 ActualWidth 更接近真实可用宽度。
|
||||
var viewportWidth = scrollViewer.ViewportWidth;
|
||||
if (!double.IsNaN(viewportWidth) && !double.IsInfinity(viewportWidth) && viewportWidth > 0)
|
||||
{
|
||||
return viewportWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return itemsControl.ActualWidth;
|
||||
}
|
||||
|
||||
private static IEnumerable<FrameworkElement> EnumerateTrackedElements(DependencyObject root)
|
||||
{
|
||||
var childCount = VisualTreeHelper.GetChildrenCount(root);
|
||||
for (var index = 0; index < childCount; index++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(root, index);
|
||||
if (child is FrameworkElement element && GetTrackWidth(element))
|
||||
{
|
||||
yield return element;
|
||||
}
|
||||
|
||||
foreach (var descendant in EnumerateTrackedElements(child))
|
||||
{
|
||||
yield return descendant;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static double GetTrackedWidth(FrameworkElement element)
|
||||
{
|
||||
var width = element.ActualWidth;
|
||||
if (width <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
var maxWidth = element.MaxWidth;
|
||||
if (!double.IsNaN(maxWidth) && !double.IsInfinity(maxWidth) && maxWidth > 0)
|
||||
{
|
||||
width = Math.Min(width, maxWidth);
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
private static double ClampWidth(ItemsControl itemsControl, double width)
|
||||
{
|
||||
if (width <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 共享宽度任何时候都不允许超过当前的单卡上限。
|
||||
var widthLimit = GetWidthLimit(itemsControl);
|
||||
if (!double.IsNaN(widthLimit) && !double.IsInfinity(widthLimit) && widthLimit > 0)
|
||||
{
|
||||
width = Math.Min(width, widthLimit);
|
||||
}
|
||||
|
||||
return Math.Max(0, width);
|
||||
}
|
||||
|
||||
private static T? FindDescendant<T>(DependencyObject root)
|
||||
where T : DependencyObject
|
||||
{
|
||||
var childCount = VisualTreeHelper.GetChildrenCount(root);
|
||||
for (var index = 0; index < childCount; index++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(root, index);
|
||||
if (child is T result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
var descendant = FindDescendant<T>(child);
|
||||
if (descendant != null)
|
||||
{
|
||||
return descendant;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -74,6 +75,8 @@ public class ToolboxViewModel : Screen
|
||||
_gachaTimer.Tick += RefreshGachaTip;
|
||||
LoadDepotDetails();
|
||||
LoadOperBoxDetails();
|
||||
InitializeDepotRowPresentation();
|
||||
InitializeOperBoxRowPresentation();
|
||||
OperBoxSelectedIndex = OperBoxNotHaveList.Count > 0 ? 0 : 1;
|
||||
|
||||
UpdateMiniGameTaskList();
|
||||
@@ -497,6 +500,8 @@ public class ToolboxViewModel : Screen
|
||||
}
|
||||
}
|
||||
|
||||
private const int DepotRowSize = 5;
|
||||
|
||||
private ObservableCollection<DepotResultDate> _depotResult = [];
|
||||
|
||||
/// <summary>
|
||||
@@ -506,11 +511,31 @@ public class ToolboxViewModel : Screen
|
||||
{
|
||||
get => _depotResult;
|
||||
set {
|
||||
if (ReferenceEquals(_depotResult, value))
|
||||
{
|
||||
RefreshDepotRows();
|
||||
InvalidateDepotCache();
|
||||
return;
|
||||
}
|
||||
|
||||
_depotResult.CollectionChanged -= DepotResultCollectionChanged;
|
||||
SetAndNotify(ref _depotResult, value);
|
||||
_depotResult.CollectionChanged += DepotResultCollectionChanged;
|
||||
RefreshDepotRows();
|
||||
InvalidateDepotCache();
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<ObservableCollection<DepotResultDate>> _depotRows = [];
|
||||
|
||||
public ObservableCollection<ObservableCollection<DepotResultDate>> DepotRows
|
||||
{
|
||||
get => _depotRows;
|
||||
private set => SetAndNotify(ref _depotRows, value);
|
||||
}
|
||||
|
||||
public int DepotColumnCount => GetColumnCount(DepotResult.Count, DepotRowSize);
|
||||
|
||||
// 缓存相关字段
|
||||
private bool _depotCacheInvalid = true;
|
||||
private string? _cachedArkPlannerResult;
|
||||
@@ -554,6 +579,23 @@ public class ToolboxViewModel : Screen
|
||||
public string? DisplayCount => Count >= 0 ? Count.FormatNumber(false) : null;
|
||||
}
|
||||
|
||||
private void InitializeDepotRowPresentation()
|
||||
{
|
||||
_depotResult.CollectionChanged += DepotResultCollectionChanged;
|
||||
RefreshDepotRows();
|
||||
}
|
||||
|
||||
private void DepotResultCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
RefreshDepotRows();
|
||||
}
|
||||
|
||||
private void RefreshDepotRows()
|
||||
{
|
||||
DepotRows = BuildRows(DepotResult, DepotRowSize);
|
||||
NotifyOfPropertyChange(nameof(DepotColumnCount));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存仓库详情数据
|
||||
/// </summary>
|
||||
@@ -1142,20 +1184,111 @@ public class ToolboxViewModel : Screen
|
||||
}
|
||||
}
|
||||
|
||||
private const int OperBoxRowSize = 5;
|
||||
|
||||
private ObservableCollection<Operator> _operBoxHaveList = [];
|
||||
|
||||
public ObservableCollection<Operator> OperBoxHaveList
|
||||
{
|
||||
get => _operBoxHaveList;
|
||||
set => SetAndNotify(ref _operBoxHaveList, value);
|
||||
set {
|
||||
if (ReferenceEquals(_operBoxHaveList, value))
|
||||
{
|
||||
RefreshOperBoxHaveRows();
|
||||
return;
|
||||
}
|
||||
|
||||
_operBoxHaveList.CollectionChanged -= OperBoxHaveListCollectionChanged;
|
||||
SetAndNotify(ref _operBoxHaveList, value);
|
||||
_operBoxHaveList.CollectionChanged += OperBoxHaveListCollectionChanged;
|
||||
RefreshOperBoxHaveRows();
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<ObservableCollection<Operator>> _operBoxHaveRows = [];
|
||||
|
||||
public ObservableCollection<ObservableCollection<Operator>> OperBoxHaveRows
|
||||
{
|
||||
get => _operBoxHaveRows;
|
||||
private set => SetAndNotify(ref _operBoxHaveRows, value);
|
||||
}
|
||||
|
||||
public int OperBoxHaveColumnCount => GetColumnCount(OperBoxHaveList.Count, OperBoxRowSize);
|
||||
|
||||
private ObservableCollection<Operator> _operBoxNotHaveList = [];
|
||||
|
||||
public ObservableCollection<Operator> OperBoxNotHaveList
|
||||
{
|
||||
get => _operBoxNotHaveList;
|
||||
set => SetAndNotify(ref _operBoxNotHaveList, value);
|
||||
set {
|
||||
if (ReferenceEquals(_operBoxNotHaveList, value))
|
||||
{
|
||||
RefreshOperBoxNotHaveRows();
|
||||
return;
|
||||
}
|
||||
|
||||
_operBoxNotHaveList.CollectionChanged -= OperBoxNotHaveListCollectionChanged;
|
||||
SetAndNotify(ref _operBoxNotHaveList, value);
|
||||
_operBoxNotHaveList.CollectionChanged += OperBoxNotHaveListCollectionChanged;
|
||||
RefreshOperBoxNotHaveRows();
|
||||
}
|
||||
}
|
||||
|
||||
private ObservableCollection<ObservableCollection<Operator>> _operBoxNotHaveRows = [];
|
||||
|
||||
public ObservableCollection<ObservableCollection<Operator>> OperBoxNotHaveRows
|
||||
{
|
||||
get => _operBoxNotHaveRows;
|
||||
private set => SetAndNotify(ref _operBoxNotHaveRows, value);
|
||||
}
|
||||
|
||||
public int OperBoxNotHaveColumnCount => GetColumnCount(OperBoxNotHaveList.Count, OperBoxRowSize);
|
||||
|
||||
private void InitializeOperBoxRowPresentation()
|
||||
{
|
||||
_operBoxHaveList.CollectionChanged += OperBoxHaveListCollectionChanged;
|
||||
_operBoxNotHaveList.CollectionChanged += OperBoxNotHaveListCollectionChanged;
|
||||
RefreshOperBoxHaveRows();
|
||||
RefreshOperBoxNotHaveRows();
|
||||
}
|
||||
|
||||
private void OperBoxHaveListCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
RefreshOperBoxHaveRows();
|
||||
}
|
||||
|
||||
private void OperBoxNotHaveListCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
RefreshOperBoxNotHaveRows();
|
||||
}
|
||||
|
||||
private void RefreshOperBoxHaveRows()
|
||||
{
|
||||
OperBoxHaveRows = BuildRows(OperBoxHaveList, OperBoxRowSize);
|
||||
NotifyOfPropertyChange(nameof(OperBoxHaveColumnCount));
|
||||
}
|
||||
|
||||
private void RefreshOperBoxNotHaveRows()
|
||||
{
|
||||
OperBoxNotHaveRows = BuildRows(OperBoxNotHaveList, OperBoxRowSize);
|
||||
NotifyOfPropertyChange(nameof(OperBoxNotHaveColumnCount));
|
||||
}
|
||||
|
||||
private static ObservableCollection<ObservableCollection<T>> BuildRows<T>(IEnumerable<T> items, int rowSize)
|
||||
{
|
||||
ObservableCollection<ObservableCollection<T>> rows = [];
|
||||
|
||||
foreach (var row in items.Chunk(rowSize))
|
||||
{
|
||||
rows.Add(new ObservableCollection<T>(row));
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static int GetColumnCount(int count, int rowSize)
|
||||
{
|
||||
return count <= 0 ? 1 : Math.Min(count, rowSize);
|
||||
}
|
||||
|
||||
private void SaveOperBoxDetails(List<OperBoxData.OperData> details)
|
||||
|
||||
@@ -16,6 +16,54 @@
|
||||
d:DesignHeight="600"
|
||||
d:DesignWidth="800"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="OperBoxVirtualizedRowListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListBoxItem}">
|
||||
<ContentPresenter
|
||||
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<Style x:Key="OperBoxVirtualizedRowListBoxStyle" TargetType="{x:Type ListBox}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center" />
|
||||
<Setter Property="ItemContainerStyle" Value="{StaticResource OperBoxVirtualizedRowListBoxItemStyle}" />
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="True" />
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled" />
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto" />
|
||||
<Setter Property="VirtualizingPanel.IsVirtualizing" Value="True" />
|
||||
<Setter Property="VirtualizingPanel.ScrollUnit" Value="Pixel" />
|
||||
<Setter Property="VirtualizingPanel.VirtualizationMode" Value="Recycling" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ListBox}">
|
||||
<hc:ScrollViewer
|
||||
Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}"
|
||||
CanContentScroll="{TemplateBinding ScrollViewer.CanContentScroll}"
|
||||
Focusable="False"
|
||||
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
|
||||
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}">
|
||||
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
|
||||
</hc:ScrollViewer>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<TabControl
|
||||
Margin="0,10"
|
||||
Background="{DynamicResource MouseOverRegionBrushOpacity75}"
|
||||
@@ -248,129 +296,149 @@
|
||||
Header="{c:Binding OperBoxNotHaveList.Count}"
|
||||
HeaderStringFormat="{StaticResource OperBoxNotHaveList}"
|
||||
Visibility="{c:Binding 'OperBoxNotHaveList.Count != 0'}">
|
||||
<hc:ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
d:ItemsSource="{d:SampleData ItemCount=1}"
|
||||
ItemsSource="{Binding OperBoxNotHaveList}"
|
||||
ScrollViewer.CanContentScroll="True"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{c:Binding 'OperBoxNotHaveList.Count >= 5 ? 5 : OperBoxNotHaveList.Count'}" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="5"
|
||||
Padding="10,15"
|
||||
Background="{DynamicResource RegionBrushOpacity50}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="13"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
uiBehaviors:ResourceReferenceHelper.ForegroundResourceKey="{Binding RarityColorResourceKey}"
|
||||
FontSize="11"
|
||||
Text="{Binding RarityStars}"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</hc:ScrollViewer>
|
||||
<ListBox
|
||||
d:ItemsSource="{d:SampleData ItemCount=3}"
|
||||
uiBehaviors:SharedCardWidthBehavior.ColumnCount="{Binding OperBoxNotHaveColumnCount}"
|
||||
uiBehaviors:SharedCardWidthBehavior.EnableScope="True"
|
||||
ItemsSource="{Binding OperBoxNotHaveRows}"
|
||||
Style="{StaticResource OperBoxVirtualizedRowListBoxStyle}">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ItemsControl HorizontalAlignment="Center" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{Binding DataContext.OperBoxNotHaveColumnCount, RelativeSource={RelativeSource AncestorType=ListBox}}" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
MinWidth="{Binding Path=(uiBehaviors:SharedCardWidthBehavior.SharedMinWidth), RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
MaxWidth="{Binding Path=(uiBehaviors:SharedCardWidthBehavior.WidthLimit), RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
Margin="5"
|
||||
Padding="10,15"
|
||||
uiBehaviors:SharedCardWidthBehavior.TrackWidth="True"
|
||||
Background="{DynamicResource RegionBrushOpacity50}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="13"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
uiBehaviors:ResourceReferenceHelper.ForegroundResourceKey="{Binding RarityColorResourceKey}"
|
||||
FontSize="11"
|
||||
Text="{Binding RarityStars}"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</TabItem>
|
||||
<TabItem Header="{c:Binding OperBoxHaveList.Count}" HeaderStringFormat="{StaticResource OperBoxHaveList}">
|
||||
<hc:ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
d:ItemsSource="{d:SampleData ItemCount=1}"
|
||||
ItemsSource="{Binding OperBoxHaveList}"
|
||||
ScrollViewer.CanContentScroll="True"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{c:Binding 'OperBoxHaveList.Count >= 5 ? 5 : OperBoxHaveList.Count'}" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="5"
|
||||
Padding="10,15"
|
||||
Background="{DynamicResource RegionBrushOpacity25}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="13"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
uiBehaviors:ResourceReferenceHelper.ForegroundResourceKey="{Binding RarityColorResourceKey}"
|
||||
FontSize="11"
|
||||
Text="{Binding RarityStars}"
|
||||
TextAlignment="Center" />
|
||||
<StackPanel
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Image
|
||||
Width="18"
|
||||
Height="18"
|
||||
Source="{Binding EliteIconPath}"
|
||||
Stretch="Uniform"
|
||||
ToolTip="{Binding Elite}">
|
||||
<Image.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="8"
|
||||
Opacity="0.5"
|
||||
ShadowDepth="2"
|
||||
Color="Black" />
|
||||
</Image.Effect>
|
||||
</Image>
|
||||
<controls:TextBlock
|
||||
Margin="2.5,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Text="{Binding Level, StringFormat={}Lv.{0}}" />
|
||||
<Image
|
||||
Width="18"
|
||||
Height="18"
|
||||
Source="{Binding PotentialIconPath}"
|
||||
ToolTip="{Binding Potential}">
|
||||
<Image.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="8"
|
||||
Opacity="0.5"
|
||||
ShadowDepth="2"
|
||||
Color="Black" />
|
||||
</Image.Effect>
|
||||
</Image>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</hc:ScrollViewer>
|
||||
<ListBox
|
||||
d:ItemsSource="{d:SampleData ItemCount=3}"
|
||||
uiBehaviors:SharedCardWidthBehavior.ColumnCount="{Binding OperBoxHaveColumnCount}"
|
||||
uiBehaviors:SharedCardWidthBehavior.EnableScope="True"
|
||||
ItemsSource="{Binding OperBoxHaveRows}"
|
||||
Style="{StaticResource OperBoxVirtualizedRowListBoxStyle}">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ItemsControl HorizontalAlignment="Center" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{Binding DataContext.OperBoxHaveColumnCount, RelativeSource={RelativeSource AncestorType=ListBox}}" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
MinWidth="{Binding Path=(uiBehaviors:SharedCardWidthBehavior.SharedMinWidth), RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
MaxWidth="{Binding Path=(uiBehaviors:SharedCardWidthBehavior.WidthLimit), RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
Margin="5"
|
||||
Padding="10,15"
|
||||
uiBehaviors:SharedCardWidthBehavior.TrackWidth="True"
|
||||
Background="{DynamicResource RegionBrushOpacity25}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="13"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
uiBehaviors:ResourceReferenceHelper.ForegroundResourceKey="{Binding RarityColorResourceKey}"
|
||||
FontSize="11"
|
||||
Text="{Binding RarityStars}"
|
||||
TextAlignment="Center" />
|
||||
<StackPanel
|
||||
Margin="0,0,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Image
|
||||
Width="18"
|
||||
Height="18"
|
||||
Source="{Binding EliteIconPath}"
|
||||
Stretch="Uniform"
|
||||
ToolTip="{Binding Elite}">
|
||||
<Image.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="8"
|
||||
Opacity="0.5"
|
||||
ShadowDepth="2"
|
||||
Color="Black" />
|
||||
</Image.Effect>
|
||||
</Image>
|
||||
<controls:TextBlock
|
||||
Margin="2.5,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Text="{Binding Level, StringFormat={}Lv.{0}}" />
|
||||
<Image
|
||||
Width="18"
|
||||
Height="18"
|
||||
Source="{Binding PotentialIconPath}"
|
||||
ToolTip="{Binding Potential}">
|
||||
<Image.Effect>
|
||||
<DropShadowEffect
|
||||
BlurRadius="8"
|
||||
Opacity="0.5"
|
||||
ShadowDepth="2"
|
||||
Color="Black" />
|
||||
</Image.Effect>
|
||||
</Image>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
<StackPanel
|
||||
@@ -427,56 +495,64 @@
|
||||
TextWrapping="Wrap"
|
||||
Visibility="{c:Binding 'DepotInfo.Length > 0'}" />
|
||||
</StackPanel>
|
||||
<hc:ScrollViewer
|
||||
<ListBox
|
||||
Grid.Row="1"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
d:ItemsSource="{d:SampleData ItemCount=1}"
|
||||
ItemsSource="{Binding DepotResult}"
|
||||
ScrollViewer.CanContentScroll="True"
|
||||
VirtualizingPanel.IsVirtualizing="True"
|
||||
VirtualizingPanel.VirtualizationMode="Recycling">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{c:Binding 'DepotResult.Count >= 5 ? 5 : DepotResult.Count'}" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
MinWidth="125"
|
||||
Margin="5"
|
||||
Padding="2"
|
||||
Background="{DynamicResource RegionBrushOpacity25}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Vertical">
|
||||
<Image
|
||||
Width="50"
|
||||
Height="50"
|
||||
Source="{Binding Image}" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding DisplayCount}"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</hc:ScrollViewer>
|
||||
d:ItemsSource="{d:SampleData ItemCount=3}"
|
||||
uiBehaviors:SharedCardWidthBehavior.ColumnCount="{Binding DepotColumnCount}"
|
||||
uiBehaviors:SharedCardWidthBehavior.EnableScope="True"
|
||||
uiBehaviors:SharedCardWidthBehavior.InitialWidth="125"
|
||||
ItemsSource="{Binding DepotRows}"
|
||||
Style="{StaticResource OperBoxVirtualizedRowListBoxStyle}">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<VirtualizingStackPanel />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ItemsControl HorizontalAlignment="Center" ItemsSource="{Binding}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<UniformGrid Columns="{Binding DataContext.DepotColumnCount, RelativeSource={RelativeSource AncestorType=ListBox}}" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
MinWidth="{Binding Path=(uiBehaviors:SharedCardWidthBehavior.SharedMinWidth), RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
MaxWidth="{Binding Path=(uiBehaviors:SharedCardWidthBehavior.WidthLimit), RelativeSource={RelativeSource AncestorType=ListBox}}"
|
||||
Margin="5"
|
||||
Padding="2"
|
||||
uiBehaviors:SharedCardWidthBehavior.TrackWidth="True"
|
||||
Background="{DynamicResource RegionBrushOpacity25}"
|
||||
BorderBrush="{DynamicResource BorderBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Vertical">
|
||||
<Image
|
||||
Width="50"
|
||||
Height="50"
|
||||
Source="{Binding Image}" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{Binding Name}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
<controls:TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding DisplayCount}"
|
||||
TextAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="0,10,0,0"
|
||||
|
||||
Reference in New Issue
Block a user