- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我正在尝试将 string.Format
用作 WPF 中的一个方便的函数,以便可以在纯 XAML 中组合各种文本部分,而无需代码隐藏中的样板。主要问题是支持函数的参数来自其他嵌套标记扩展(例如 Binding
)的情况。
实际上,有一个功能非常接近我需要的功能: MultiBinding
。不幸的是,它只能接受绑定(bind),而不能接受其他动态类型的内容,例如 DynamicResource
s。
如果我所有的数据源都是绑定(bind),我可以使用这样的标记:
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource StringFormatConverter}">
<Binding Path="FormatString"/>
<Binding Path="Arg0"/>
<Binding Path="Arg1"/>
<!-- ... -->
</MultiBinding>
</TextBlock.Text>
</TextBlock>
StringFormatConveter
。
<TextBlock>
<TextBlock.Text>
<l:StringFormat Format="{Binding FormatString}">
<DynamicResource ResourceKey="ARG0ID"/>
<Binding Path="Arg1"/>
<StaticResource ResourceKey="ARG2ID"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{l:StringFormat {Binding FormatString},
arg0={DynamicResource ARG0ID},
arg1={Binding Arg2},
arg2='literal string', ...}"/>
ProvideValue(IServiceProvider serviceProvider)
的实现。
serviceProvider
,要么查询
IProvideValueTarget
,它(主要)说明了标记扩展的目标是什么依赖属性。在任何情况下,代码都知道应该在
ProvideValue
调用时提供的值。但是,
ProvideValue
只会被调用一次(
except for templates ,这是一个单独的故事),因此如果实际值不是恒定的(例如
Binding
等),则应使用另一种策略。
Binding
的实现,它的
ProvideValue
方法实际上返回的不是真正的目标对象,而是
System.Windows.Data.BindingExpression
类的一个实例,它似乎做了所有的实际工作。关于
DynamicResource
也是如此:它只返回
System.Windows.ResourceReferenceExpression
的一个实例,它关心订阅(内部)
InheritanceContextChanged
并在适当的时候使该值无效。但是,通过查看代码,我无法理解以下内容:
BindingExpression
/ResourceReferenceExpression
的对象未被“按原样”处理,而是被要求提供基础值,这是怎么回事? MultiBindingExpression
如何知道底层绑定(bind)的值发生了变化,因此它也必须使其值失效? Binding
)。
System.Windows.Expression
具有内部构造函数)?
<l:FormatHelper x:Name="h1" Format="{DynamicResource FORMAT_ID'">
<l:FormatArgument Value="{Binding Data1}"/>
<l:FormatArgument Value="{StaticResource Data2}"/>
</l:FormatHelper>
<TextBlock Text="{Binding Value, ElementName=h1}"/>
FormatHelper
跟踪其子项及其依赖项属性更新,并将最新结果存储到
Value
中),但这种语法似乎很难看,我想摆脱可视化树中的辅助项。
ResourceDictionary
并在语言改变时将被替换)和
Binding
到表示时间的 VM 依赖属性。
var result = new MultiBinding()
{
Converter = new StringFormatConverter(),
Mode = BindingMode.OneWay
};
foreach (var v in values)
{
if (v is MarkupExtension)
{
var b = v as Binding;
if (b != null)
{
result.Bindings.Add(b);
continue;
}
var bb = v as BindingBase;
if (bb != null)
{
targetObjFE.SetBinding(AddBindingTo(targetObjFE, result), bb);
continue;
}
}
if (v is System.Windows.Expression)
{
DynamicResourceExtension mex = null;
// didn't find other way to check for dynamic resource
try
{
// rrc is a new ResourceReferenceExpressionConverter();
mex = (MarkupExtension)rrc.ConvertTo(v, typeof(MarkupExtension))
as DynamicResourceExtension;
}
catch (Exception)
{
}
if (mex != null)
{
targetObjFE.SetResourceReference(
AddBindingTo(targetObjFE, result),
mex.ResourceKey);
continue;
}
}
// fallback
result.Bindings.Add(
new Binding() { Mode = BindingMode.OneWay, Source = v });
}
return result.ProvideValue(serviceProvider);
targetObj
获得的
IProvideValueTarget
是
null
。我试图通过将嵌套绑定(bind)合并到外部绑定(bind)(
[1a] 、
[2a] )来解决这个问题(将多绑定(bind)溢出添加到外部绑定(bind)中),这可能适用于嵌套的多绑定(bind)和格式扩展,但仍然无法使用嵌套的动态资源。
Binding
s 和
MultiBinding
s,但是
ResourceReferenceExpression
而不是
DynamicResourceExtension
。我想知道为什么它不一致(以及如何从
Binding
重建
BindingExpression
)。
最佳答案
看看以下是否适合你。我拿了test case您在评论中提供并稍微扩展以更好地说明该机制。我想关键是通过使用 DependencyProperties
来保持灵活性。在嵌套容器中。
编辑 :我已经用 TextBlock 的子类替换了混合行为。这为 DataContext 和 DynamicResources 添加了更简单的链接。
在旁注中,您的项目使用的方式 DynamicResources
引入条件不是我推荐的。而是尝试使用 ViewModel 来建立条件,和/或使用触发器。
Xml:
<UserControl x:Class="WpfApplication1.Controls.ExpiryView" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:props="clr-namespace:WpfApplication1.Properties" xmlns:models="clr-namespace:WpfApplication1.Models"
xmlns:h="clr-namespace:WpfApplication1.Helpers" xmlns:c="clr-namespace:WpfApplication1.CustomControls"
Background="#FCF197" FontFamily="Segoe UI"
TextOptions.TextFormattingMode="Display"> <!-- please notice the effect of this on font fuzzyness -->
<UserControl.DataContext>
<models:ExpiryViewModel />
</UserControl.DataContext>
<UserControl.Resources>
<system:String x:Key="ShortOrLongDateFormat">{0:d}</system:String>
</UserControl.Resources>
<Grid>
<StackPanel>
<c:TextBlockComplex VerticalAlignment="Center" HorizontalAlignment="Center">
<c:TextBlockComplex.Content>
<h:StringFormatContainer StringFormat="{x:Static props:Resources.ExpiryDate}">
<h:StringFormatContainer.Values>
<h:StringFormatContainer Value="{Binding ExpiryDate}" StringFormat="{DynamicResource ShortOrLongDateFormat}" />
<h:StringFormatContainer Value="{Binding SecondsToExpiry}" />
</h:StringFormatContainer.Values>
</h:StringFormatContainer>
</c:TextBlockComplex.Content>
</c:TextBlockComplex>
</StackPanel>
</Grid>
</UserControl>
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using WpfApplication1.Helpers;
namespace WpfApplication1.CustomControls
{
public class TextBlockComplex : TextBlock
{
// Content
public StringFormatContainer Content { get { return (StringFormatContainer)GetValue(ContentProperty); } set { SetValue(ContentProperty, value); } }
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(StringFormatContainer), typeof(TextBlockComplex), new PropertyMetadata(null));
private static readonly DependencyPropertyDescriptor _dpdValue = DependencyPropertyDescriptor.FromProperty(StringFormatContainer.ValueProperty, typeof(StringFormatContainer));
private static readonly DependencyPropertyDescriptor _dpdValues = DependencyPropertyDescriptor.FromProperty(StringFormatContainer.ValuesProperty, typeof(StringFormatContainer));
private static readonly DependencyPropertyDescriptor _dpdStringFormat = DependencyPropertyDescriptor.FromProperty(StringFormatContainer.StringFormatProperty, typeof(StringFormatContainer));
private static readonly DependencyPropertyDescriptor _dpdContent = DependencyPropertyDescriptor.FromProperty(TextBlockComplex.ContentProperty, typeof(StringFormatContainer));
private EventHandler _valueChangedHandler;
private NotifyCollectionChangedEventHandler _valuesChangedHandler;
protected override IEnumerator LogicalChildren { get { yield return Content; } }
static TextBlockComplex()
{
// take default style from TextBlock
DefaultStyleKeyProperty.OverrideMetadata(typeof(TextBlockComplex), new FrameworkPropertyMetadata(typeof(TextBlock)));
}
public TextBlockComplex()
{
_valueChangedHandler = delegate { AddListeners(this.Content); UpdateText(); };
_valuesChangedHandler = delegate { AddListeners(this.Content); UpdateText(); };
this.Loaded += TextBlockComplex_Loaded;
}
void TextBlockComplex_Loaded(object sender, RoutedEventArgs e)
{
OnContentChanged(this, EventArgs.Empty); // initial call
_dpdContent.AddValueChanged(this, _valueChangedHandler);
this.Unloaded += delegate { _dpdContent.RemoveValueChanged(this, _valueChangedHandler); };
}
/// <summary>
/// Reacts to a new topmost StringFormatContainer
/// </summary>
private void OnContentChanged(object sender, EventArgs e)
{
this.AddLogicalChild(this.Content); // inherits DataContext
_valueChangedHandler(this, EventArgs.Empty);
}
/// <summary>
/// Updates Text to the Content values
/// </summary>
private void UpdateText()
{
this.Text = Content.GetValue() as string;
}
/// <summary>
/// Attaches listeners for changes in the Content tree
/// </summary>
private void AddListeners(StringFormatContainer cont)
{
// in case they have been added before
RemoveListeners(cont);
// listen for changes to values collection
cont.CollectionChanged += _valuesChangedHandler;
// listen for changes in the bindings of the StringFormatContainer
_dpdValue.AddValueChanged(cont, _valueChangedHandler);
_dpdValues.AddValueChanged(cont, _valueChangedHandler);
_dpdStringFormat.AddValueChanged(cont, _valueChangedHandler);
// prevent memory leaks
cont.Unloaded += delegate { RemoveListeners(cont); };
foreach (var c in cont.Values) AddListeners(c); // recursive
}
/// <summary>
/// Detaches listeners
/// </summary>
private void RemoveListeners(StringFormatContainer cont)
{
cont.CollectionChanged -= _valuesChangedHandler;
_dpdValue.RemoveValueChanged(cont, _valueChangedHandler);
_dpdValues.RemoveValueChanged(cont, _valueChangedHandler);
_dpdStringFormat.RemoveValueChanged(cont, _valueChangedHandler);
}
}
}
using System.Linq;
using System.Collections;
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1.Helpers
{
public class StringFormatContainer : FrameworkElement
{
// Values
private static readonly DependencyPropertyKey ValuesPropertyKey = DependencyProperty.RegisterReadOnly("Values", typeof(ObservableCollection<StringFormatContainer>), typeof(StringFormatContainer), new FrameworkPropertyMetadata(new ObservableCollection<StringFormatContainer>()));
public static readonly DependencyProperty ValuesProperty = ValuesPropertyKey.DependencyProperty;
public ObservableCollection<StringFormatContainer> Values { get { return (ObservableCollection<StringFormatContainer>)GetValue(ValuesProperty); } }
// StringFormat
public static readonly DependencyProperty StringFormatProperty = DependencyProperty.Register("StringFormat", typeof(string), typeof(StringFormatContainer), new PropertyMetadata(default(string)));
public string StringFormat { get { return (string)GetValue(StringFormatProperty); } set { SetValue(StringFormatProperty, value); } }
// Value
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(object), typeof(StringFormatContainer), new PropertyMetadata(default(object)));
public object Value { get { return (object)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } }
public StringFormatContainer()
: base()
{
SetValue(ValuesPropertyKey, new ObservableCollection<StringFormatContainer>());
this.Values.CollectionChanged += OnValuesChanged;
}
/// <summary>
/// The implementation of LogicalChildren allows for DataContext propagation.
/// This way, the DataContext needs only be set on the outermost instance of StringFormatContainer.
/// </summary>
void OnValuesChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (var value in e.NewItems)
AddLogicalChild(value);
}
if (e.OldItems != null)
{
foreach (var value in e.OldItems)
RemoveLogicalChild(value);
}
}
/// <summary>
/// Recursive function to piece together the value from the StringFormatContainer hierarchy
/// </summary>
public object GetValue()
{
object value = null;
if (this.StringFormat != null)
{
// convention: if StringFormat is set, Values take precedence over Value
if (this.Values.Any())
value = string.Format(this.StringFormat, this.Values.Select(v => (object)v.GetValue()).ToArray());
else if (Value != null)
value = string.Format(this.StringFormat, Value);
}
else
{
// convention: if StringFormat is not set, Value takes precedence over Values
if (Value != null)
value = Value;
else if (this.Values.Any())
value = string.Join(string.Empty, this.Values);
}
return value;
}
protected override IEnumerator LogicalChildren
{
get
{
if (Values == null) yield break;
foreach (var v in Values) yield return v;
}
}
}
}
using System;
using System.ComponentModel;
namespace WpfApplication1.Models
{
public class ExpiryViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private DateTime _expiryDate;
public DateTime ExpiryDate { get { return _expiryDate; } set { _expiryDate = value; OnPropertyChanged("ExpiryDate"); } }
public int SecondsToExpiry { get { return (int)ExpiryDate.Subtract(DateTime.Now).TotalSeconds; } }
public ExpiryViewModel()
{
this.ExpiryDate = DateTime.Today.AddDays(2.67);
var timer = new System.Timers.Timer(1000);
timer.Elapsed += (s, e) => OnPropertyChanged("SecondsToExpiry");
timer.Start();
}
}
}
关于c# - 带有标记扩展名的字符串格式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25415400/
我想使用 li 和 ul 制作一个多级下拉列表,以便显示我博客中按年和月排序的所有文章。我希望我的下拉菜单看起来像 Google Blogspot 下拉菜单: 这是我的 CSS 和 HTML 代码 u
我在 Win 7 64 机器上将 CodeBlocks 与 gcc 4.7.2 和 gmp 5.0.5 结合使用。开始使用 gmpxx 后,我看到一个奇怪的段错误,它不会出现在 +、- 等运算符中,但
我正在使用 tern 为使用 CodeMirror 运行的窗口提供一些增强的智能感知,它工作正常,但我遇到了一个问题,我想添加一些自定义“types”,可以这么说,这样下拉列表中它们旁边就有图标了。我
我正在尝试让我的 PC 成为 Android 2.3.4 设备的 USB 主机,以便能够在不需要实际“附件”的情况下开发 API。为此,我需要将 PC 设置为 USB 主机和“设备”(在我的例子中是运
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 9
我在设置服务器方面几乎是个新手,但遇到了一个问题。我有一个 Ubuntu 16.04 VPS 并安装了 Apache2 和 Tomcat7。我正在为 SSL 使用 LetsEncrypt 和 Cert
我在一个基于谷歌地图的项目上工作了超过 6 个月。我使用的是 Google Maps API V1 及其开发人员 API key 。当我尝试发布应用程序时,我了解到 Google API V1 已被弃
我是 Python 的新手,所以如果我对一些简单的事情感到困惑,请原谅。 我有一个这样的对象: class myObject(object): def __init__(self):
这个问题已经有答案了: How can I access object properties containing special characters? (2 个回答) 已关闭 9 年前。 我正在尝
我有下面的 CSS。我想要的是一种流体/液体(因为缺乏正确的术语)css。我正在为移动设备开发,当我改变模式时 从纵向 View 到陆地 View ,我希望它流畅。现在的图像 在陆地 View 中效
我正在尝试使用可以接受参数的缓存属性装饰器。 我查看了这个实现:http://www.daniweb.com/software-development/python/code/217241/a-cac
这个问题在这里已经有了答案: Understanding slicing (36 个答案) 关闭 6 年前。 以a = [1,2,3,4,5]为例。根据我的直觉,我认为 a[::-1] 与 a[0:
mysqldump -t -u root -p mytestdb mytable --where=datetime LIKE '2014-09%' 这就是我正在做的事情,它会返回: mysqldum
我正在制作销售税计算器,除了总支付金额部分外,其他一切都正常。在我的程序中,我希望能够输入一个数字并获得该项目的税额我还希望能够获得支付的总金额,包括交易中的税金。到目前为止,我编写的代码完成了所有这
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许在 Stack Overflow 上提出有关通用计算硬件和软件的问题。您可以编辑问题,使其成为
我是否必须进行任何额外的设置才能让 apache-airflow 在任务失败时向我发送电子邮件。我的配置文件中有以下内容(与默认值保持不变): [email] email_backend = airf
这个问题在这里已经有了答案: What does the $ symbol do in VBA? (5 个回答) 3年前关闭。 使用返回字符串(如 Left)的内置函数有什么区别吗?或使用与 $ 相同
我有一个用VB6编写的应用程序,我需要使用一个用.NET编写的库。有什么方法可以在我的应用程序上使用该库吗? 谢谢 最佳答案 这取决于。您可以控制.NET库吗? 如果是这样,则可以修改您的库,以便可以
当我创建一个以 ^ 开头的类方法时,我尝试调用它,它给了我一个错误。 class C { method ^test () { "Hi" } } dd C.new.test; Too m
我已经使用 bower 安装了 angularjs 和 materialjs。 凉亭安装 Angular Material 并将“ngMaterial”注入(inject)我的应用程序,但出现此错误。
我是一名优秀的程序员,十分优秀!