我在 xaml 中有许多非常相似的资源(略有不同:bingings 中的属性名称、标题中的静态文本等),它们非常大且复杂:
<Window.Resource>
<A x:Key="a1"> ... </A>
<A x:Key="a2"> ... </A>
...
<B x:Key="b1"> ... />
<B x:Key="b2"> ... />
...
<C x:Key="c1"> ... />
...
</Window.Resource>
我的目标就是这样:
<A x:Key="a" ... />
<B x:Key="b" ... />
<C x:Key="c" ... />
...
资源成为一种模板。但随后我需要以某种方式定义一个参数来更改每个此类资源(例如,修改绑定(bind)中的属性名称),然后再使用它。
我当前的想法是将资源作为文本进行操作:
var xaml = @"... Text = ""{Binding %PROPERTY%}"" ...";
xaml = xaml.Replace("%PROPERTY%", realPropertyName);
view.Content = XamlReader.Parse(xaml)
但是在代码隐藏中定义 xaml 字符串听起来不太好,它们应该是使用它们的 xaml 的一部分。
所以我有了这个绝妙的想法:
// get some resource and restore xaml string for it, yay!
var xaml = XamlWriter.Save(FindResource("some resource"));
但不幸的是XamlWriter非常有限,它没有起作用,恢复这样的xaml是完全无法使用的。
然后我想到将资源定义为字符串:
<clr:String x:Key="a">...</clr:String>
但是 xaml 中的多行字符串和特殊字符使得这种方法看起来非常难看。不要在家尝试。
理想情况下,我想像以前一样定义资源(具有智能和其他东西),并且只想在运行时以某种方式修改它们,因此我的问题。
问题的本地化情况(完全相同)是在DataTemplate
中包含参数。我问的是关于dynamic columns的问题早些时候,这就是为什么我当前定义了如此多的类似资源并试图再次找到解决方案。
我忘记添加资源的具体示例以及某种形式的 MCVE:
<Window.Resources>
<GridViewColumn x:Key="column1">
<GridViewColumn.Header>
<DataTemplate>
<TextBlock Text="Header1" />
</DataTemplate>
</GridViewColumn.Header>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value1}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
...
... more similar columns
...
</Window.Resources>
<ListView x:Name="listView" ItemsSource="{Binding Items}">
<ListView.View>
<GridView />
</ListView.View>
</ListView>
添加了一些列
var view = (GridView)listView.View;
foreach(var column in Columns.Where(o => o.Show))
view.Columns.Add((GridViewColumn)FindResouce(column.Key));
其中Columns
集合定义了哪些列可以显示、哪些列隐藏、它们的宽度等。
public class Column
{
public string Key { get; set; } // e.g. "column1"
public bool Show { get; set; }
...
}
要拥有 100 列,我必须定义 100 个 "columnX"
资源,但它们非常相似。我的挑战是仅定义一个,然后以某种方式更改动态部分(在本例中将 "Header1"
更改为 "Header2"
和 "Value1"
到“Value2”
)。
最佳答案
我找到了一种编写 xaml 的方法:
有设计师支持- 具有智能感知支持;
- 可以在运行时修改。
为此,需要将 xaml(资源)放入单独的 ResourceDictionary
中,其中 Build
属性设置为 Embedded Resource
内容将如下所示:
<ResourceDictionary ...>
<GridViewColumn x:Key="test" Header="%HEADER%"> <!-- placeholder for header -->
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding %CELL%}" /> <!-- placeholder for cell property name -->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</ResourceDictionary>
以及要加载和修改的代码
// get resource stream
var element = XElement.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream("..."));
// get xaml as text for a specified x:Key
var xaml = element.Descendants().First(o => o.Attributes().Any(attribute => attribute.Name.LocalName == "Key")).ToString();
// dynamic part
xaml = xaml.Replace("%HEADER%", "Some header");
xaml = xaml.Replace("%CELL%", "SomePropertyName");
// xaml to object
var context = new ParserContext();
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
var column = (GridViewColumn)XamlReader.Parse(xaml, context);
view.Columns.Add(column);
我个人根本不使用设计器(只是为了快速导航)并用手编写所有xaml。没有设计师的支持对我来说不是问题。
智能感知支持和在编译时查看错误非常方便。忽略构建操作,xaml 将得到完全验证,这是一件好事(与将 xaml 保存在代码后面或文本文件中的 string
中)。
将资源与窗口/用户控制分离有时会出现问题,例如如果存在与 ElementName
的绑定(bind)或对其他资源的引用(未移至资源字典)等。我目前有 issue with BindingProxy ,因此该解决方案不是最终解决方案。
关于c# - 如何在运行时修改xaml资源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65038699/