You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Grid的RowDefinition Height设置DynamicResource?

问题原因分析

1. DynamicResource在RowDefinition.Height上的限制

Xamarin.Forms中,RowDefinition.Height虽为可绑定属性,但Grid的行定义集合(RowDefinitions)初始化后,不会自动监听单个RowDefinition的Height属性变更通知。像Label.TextColor这类视觉元素属性,继承自VisualElement,DynamicResource变更时会触发属性变更进而更新UI;但RowDefinition不属于视觉元素范畴,其属性变更无法触发Grid的重新布局。

2. 转换器方案的缺陷

你的转换器仅在绑定初始化时执行一次,直接读取当时的RowHeight资源值,但没有监听资源的后续变更。因为绑定的Source是DynamicResource对象本身,该对象不会随资源值变化而改变,所以转换器不会重新执行,导致后续行高更新无法生效。

可行解决方案

方案1:手动触发Grid布局更新

更新RowHeight资源后,手动调用Grid的ForceLayout()方法,强制Grid重新计算布局。

修改App.xaml.cs中的UpdateResources方法:

private async void UpdateResources()
{
    Console.WriteLine("Wait 5 seconds");
    await System.Threading.Tasks.Task.Delay(5000);

    Console.WriteLine("Change color to RED and height to 500");
    Resources["RowHeight"] = new GridLength(500);
    Resources["LabelColor"] = Color.Red;
    // 获取MainPage中的Grid并强制布局
    if (MainPage is MainPage mainPage)
    {
        var grid = mainPage.FindByName<Grid>("testGrid");
        grid?.ForceLayout();
    }

    Console.WriteLine("Wait 5 seconds");
    await System.Threading.Tasks.Task.Delay(5000);

    Console.WriteLine("Change color to BLUE and height to 25");
    Resources["RowHeight"] = new GridLength(25);
    Resources["LabelColor"] = Color.Blue;
    if (MainPage is MainPage mainPage2)
    {
        var grid2 = mainPage2.FindByName<Grid>("testGrid");
        grid2?.ForceLayout();
    }

    Console.WriteLine("Wait 5 seconds");
    await System.Threading.Tasks.Task.Delay(5000);

    Console.WriteLine("Change color to DeepPink and height to 100");
    Resources["RowHeight"] = new GridLength(100);
    Resources["LabelColor"] = Color.DeepPink;
    if (MainPage is MainPage mainPage3)
    {
        var grid3 = mainPage3.FindByName<Grid>("testGrid");
        grid3?.ForceLayout();
    }
}

同时给MainPage中的Grid命名:

<Grid x:Name="testGrid" BackgroundColor="Orange">

方案2:使用绑定结合ViewModel(推荐)

放弃DynamicResource,改用MVVM绑定方式,将行高绑定到ViewModel的属性,通过INotifyPropertyChanged触发更新,Grid会响应属性变更:

  1. 创建ViewModel:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace TestDynamicResource
{
    public class MainViewModel : INotifyPropertyChanged
    {
        private GridLength _rowHeight;
        public GridLength RowHeight
        {
            get => _rowHeight;
            set
            {
                _rowHeight = value;
                OnPropertyChanged();
            }
        }

        private Color _labelColor;
        public Color LabelColor
        {
            get => _labelColor;
            set
            {
                _labelColor = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
  1. 修改MainPage.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TestDynamicResource"
             x:Class="TestDynamicResource.MainPage">
    <ContentPage.BindingContext>
        <local:MainViewModel />
    </ContentPage.BindingContext>
    <StackLayout>
        <Grid BackgroundColor="Orange">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="{Binding RowHeight}" />
                <RowDefinition Height="{Binding RowHeight}"/>
            </Grid.RowDefinitions>
            <Label Grid.Row="0" Text="Testing Color Change on this one" TextColor="{Binding LabelColor}"/>
            <Label Grid.Row="1" Text="Testing Height" />
            <Label Grid.Row="2" Text="Testing Height Change on this one through binding"/>
        </Grid>
    </StackLayout>
</ContentPage>
  1. 修改App.xaml.cs中的更新逻辑:
private async void UpdateResources()
{
    var viewModel = (MainViewModel)((MainPage)MainPage).BindingContext;
    
    Console.WriteLine("Wait 5 seconds");
    await System.Threading.Tasks.Task.Delay(5000);

    Console.WriteLine("Change color to RED and height to 500");
    viewModel.RowHeight = new GridLength(500);
    viewModel.LabelColor = Color.Red;

    Console.WriteLine("Wait 5 seconds");
    await System.Threading.Tasks.Task.Delay(5000);

    Console.WriteLine("Change color to BLUE and height to 25");
    viewModel.RowHeight = new GridLength(25);
    viewModel.LabelColor = Color.Blue;

    Console.WriteLine("Wait 5 seconds");
    await System.Threading.Tasks.Task.Delay(5000);

    Console.WriteLine("Change color to DeepPink and height to 100");
    viewModel.RowHeight = new GridLength(100);
    viewModel.LabelColor = Color.DeepPink;
}

方案3:自定义可监听资源变更的RowDefinition

创建自定义RowDefinition类,监听Application资源的变更,自动更新Height属性:

using Xamarin.Forms;

namespace TestDynamicResource
{
    public class ObservableRowDefinition : RowDefinition
    {
        private string _resourceKey;
        public string ResourceKey
        {
            get => _resourceKey;
            set
            {
                if (_resourceKey != value)
                {
                    if (!string.IsNullOrEmpty(_resourceKey))
                    {
                        Application.Current.Resources.RemoveResourceChangedListener(OnResourceChanged);
                    }
                    _resourceKey = value;
                    if (!string.IsNullOrEmpty(_resourceKey))
                    {
                        Application.Current.Resources.AddResourceChangedListener(OnResourceChanged);
                        UpdateHeightFromResource();
                    }
                }
            }
        }

        private void OnResourceChanged(object sender, ResourcesChangedEventArgs e)
        {
            if (e.Key == _resourceKey)
            {
                UpdateHeightFromResource();
            }
        }

        private void UpdateHeightFromResource()
        {
            if (Application.Current.Resources.TryGetValue(_resourceKey, out var value) && value is GridLength length)
            {
                Height = length;
            }
        }
    }
}

然后在XAML中使用:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:TestDynamicResource"
             x:Class="TestDynamicResource.MainPage">
    <StackLayout>
        <Grid BackgroundColor="Orange">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <local:ObservableRowDefinition ResourceKey="RowHeight" />
                <local:ObservableRowDefinition ResourceKey="RowHeight" />
            </Grid.RowDefinitions>
            <Label Grid.Row="0" Text="Testing Color Change on this one" TextColor="{DynamicResource LabelColor}"/>
            <Label Grid.Row="1" Text="Testing Height" />
            <Label Grid.Row="2" Text="Testing Height Change on this one through binding"/>
        </Grid>
    </StackLayout>
</ContentPage>

这种方式可以保留DynamicResource的资源管理方式,同时自动响应资源变更。

总结

不是你的理解错误,而是Xamarin.Forms中RowDefinition的属性变更通知机制和视觉元素不同,导致DynamicResource的变更无法自动触发Grid布局更新。上述方案中,MVVM绑定的方式最符合Xamarin.Forms的设计规范,自定义ObservableRowDefinition则能保留资源管理的灵活性。

内容的提问来源于stack exchange,提问作者Ibrennan208

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 20:55:23