Xaml ContentDialog和x:Bind的问题
我有一个Xaml ContentDialog和x:Bind的问题,xaml,uwp,uwp-xaml,Xaml,Uwp,Uwp Xaml,我有一个ContentDialog,它实现了INotifyPropertyChanged。我有一个TextBox,它的文本属性通过x:Bind语法绑定到我代码后面的字符串属性 如果我编辑文本框的内容,然后单击内容对话框中的另一个控件(失去对文本框的关注),然后单击主按钮,当我到达主按钮click事件处理程序时,文本属性已经用TextBoxcontrol=PERFECT的内容更新 但是,如果我更改了文本框的内容,但将其保持在焦点位置,然后单击主按钮,则绑定将永远不会更新 这样做的原因似乎是因为Co
ContentDialog
,它实现了INotifyPropertyChanged
。我有一个TextBox
,它的文本属性通过x:Bind
语法绑定到我代码后面的字符串属性
如果我编辑文本框的内容
,然后单击内容对话框
中的另一个控件(失去对文本框
的关注),然后单击主按钮,当我到达主按钮click事件处理程序时,文本属性已经用TextBox
control=PERFECT的内容更新
但是,如果我更改了文本框的内容,但将其保持在焦点位置,然后单击主按钮,则绑定将永远不会更新
这样做的原因似乎是因为ContentDialog
中的内置按钮在触发其click EventHandler之前不会获得焦点,并且由于x:Bind
仅支持LostFocus
绑定,这些绑定将永远不会更新
我对这个大虫子的存在感到震惊。所以我的两个问题是
1) 有解决办法吗
2) 我是否必须放弃x:Bind
并使用WPF风格的Binding
语法,这样我就可以在绑定本身中更改updatesourcerigger
我希望另一个UWP开发人员已经遇到了这一点,并知道一个解决办法
编辑
我创建了一些示例代码来演示这个问题
第页:
内容对话框:
<ContentDialog
x:Class="App1.ContentDialog1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="TITLE"
PrimaryButtonText="Button1"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick">
<Grid>
<TextBox Text="{x:Bind Path=TestText, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="100"/>
</Grid>
</ContentDialog>
我的目标sdk版本是:
如果单击“打开内容”对话框,请输入一些文本,然后单击主按钮-如果单击处理程序中有一个断点,您将看到绑定没有使用输入的文本更新。我无法重现该问题:代码隐藏中的字符串属性的setter总是在内容对话框\u主按钮单击之前启动,即使在焦点仍在文本框上时单击主按钮
。我的样本(在秋季创作者更新中测试):
MyContentDialog.xaml
DTO.cs
MainPage.xaml.cs
你能分享你的代码吗?您的项目的最小版本和目标版本是什么?好的,我已经研究了这个问题,并在所有SDK的back to build 10586上进行了测试。本质上,ContentDialog
对于x:Bind
到TextBox
的绑定被破坏,因为TextBox
需要失去焦点才能更新其绑定。
我已经尝试了很多方法,包括在点击处理程序触发之前,在加载的上遍历视觉树,向隐藏按钮添加额外的处理程序,并尝试强制它们聚焦(从而从任何文本框中移除焦点,并触发绑定更新),但不支持隧道事件策略的UWP(与WPF类似),不可能在按钮按下和硬连线的click处理程序之间插入任何类型的事件处理。
结果是x:Bind
(除了LostFocus
,它不支持UpdateSourceTrigger
)无法在ContentDialog
中可靠地使用TextBox
控件
我在Microsoft uservoice上记录了一个错误
解决此问题的唯一方法是放弃x:Bind
,使用支持PropertyChanged
的UpdateSourceTrigger
的经典绑定,从而允许绑定在单击按钮之前始终是最新的。
但即使这样,这也是一种黑客行为,因为PropertyChanged
的UpdateSourceTrigger
将导致设置程序中不必要的更新,这可能会有问题。此外,在INotifyPropertyChanged
中更新设置程序将为所有布局触发多轮额外的排列/测量过程
唯一真正的解决方案是编写自己的ContentDialog
控件。我很想这样做,但由于无法访问这些控件的Microsofts源代码,很难知道您是否成功地完成了它我刚刚想出来,我花了很多时间,就像您做的一样,几乎两天都失败了,ContentDialog崩溃了y应用程序如果有空对象绑定到任何控件,都必须使用转换器
我正在考虑在对话框上放置一个usercontrol,看看绑定是否有效
让我试着把结果贴在这里
如果您找到了其他方法,请告诉我谢谢您的帮助。我已经创建了一个显示此问题的代码版本,并包含了目标SDK信息
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
}
private async void OpenClick(object sender, RoutedEventArgs e)
{
var dialog = new ContentDialog1();
await dialog.ShowAsync();
}
}
<ContentDialog
x:Class="App1.ContentDialog1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="TITLE"
PrimaryButtonText="Button1"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick">
<Grid>
<TextBox Text="{x:Bind Path=TestText, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="100"/>
</Grid>
</ContentDialog>
public sealed partial class ContentDialog1 : ContentDialog, INotifyPropertyChanged
{
public ContentDialog1()
{
InitializeComponent();
}
private string _testText;
public string TestText
{
get => _testText;
set
{
_testText = value;
OnPropertyChanged(nameof(TestText));
}
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
<Grid>
<TextBox Text="{x:Bind MyString, Mode=TwoWay}"/>
</Grid>
public sealed partial class MyContentDialog : ContentDialog, INotifyPropertyChanged
{
private DTO dto;
private string myString;
public string MyString
{
get
{
return myString;
}
set
{
myString = value;
NotifyPropertyChanged(nameof(MyString));
}
}
public MyContentDialog(DTO dto)
{
this.InitializeComponent();
this.dto = dto;
MyString = dto.text;
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
dto.text = MyString;
}
private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DTO
{
public string text;
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
DTO dto = new DTO { text = "My text" };
MyContentDialog dialog = new MyContentDialog(dto);
await dialog.ShowAsync();
await new MessageDialog("My text, after being edited by the user in MyContentDialog: " + dto.text).ShowAsync();
}