data-binding 相关问题

将两个数据/信息源绑定在一起并使它们保持同步的一般技术。

如何在不调用所有对象的 c'tor 的情况下对列表框 ItemSource 进行排序? WPF

我有以下列表框 我有以下列表框 <ListBox SelectionMode="Extended" ItemsSource="{Binding Containers}" AllowDrop="True" Margin="0,0,5,0"> <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem"> <Setter Property="IsSelected" Value="{Binding Content.IsSelected, Mode=TwoWay, RelativeSource={RelativeSource Self}}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <ContentPresenter/> </ControlTemplate> </Setter.Value> </Setter> </Style> </ListBox.ItemContainerStyle> <ListBox.ItemTemplate> <DataTemplate DataType="{x:Type vm:ContainerViewModel}"> <local:ContainerUserControl DataContext="{Binding}" /> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <VirtualizingStackPanel IsVirtualizing="True" VirtualizationMode="Recycling" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> ContainerUserControl 是具有扩展器(带有标题和内容)的用户控件。 视图模型是 ContainerViewModel 绑定的物品来源为: private ObservableCollection<ContainerViewModel> _containers; public ObservableCollection<ContainerViewModel> Containers { get => _containers; set { _containers = value; OnPropertyChanged(); } } 问题是当我为容器分配新集合时,每个元素的构造函数都被调用: public partial class ContainerUserControl : UserControl { public ContainerUserControl() { InitializeComponent(); Debug.Print("in ContainerUserControl"); } } 如果我有几千件物品,可能需要很长时间。 现在假设我有 10k 个项目,我想使用以下代码对这个集合进行排序: Containers = new ObservableCollection<ContainerViewModel>(Containers.OrderByDescending(i => i.Name)); 我会看到用户控件构造函数被调用了 10k 次。 在阅读了一些帖子后,我决定使用“移动”方法实现就地排序,但不幸的是,即使我这样做了: _containers.Move(0, 1); 我看到我通过了 userControl c'tor。如果我有数千个移动操作,就像使用 orderby 方法并分配排序列表一样。 此外,我尝试创建一个新的排序集合并在 itemsSource 之间切换,但它没有帮助,仍然输入 c'tor 10k 次。 public ObservableCollection<ContainerViewModel> SortedContainers { get; set; } // already sorted public ListBox ContainerListBox { get; set; } // the listbox from xaml ContainerListBox.ItemsSource = null; ContainerListBox.ItemsSource = SortedContainers; 无论我尝试什么,我都无法避免 c'tor 被调用数千次并造成可怕的性能问题。 我怎样才能避免 c'tor 打电话?为什么这个 c'tor 无论如何都被称为? 任何帮助将不胜感激:) ListBox使用UI虚拟化。它不会加载所有项目。只有那些在虚拟化视口内的。因为默认情况下启用虚拟化,所以覆盖 ListBox.ItemPanel 是多余的。 同样适用于 DataTemplate 内的 DataCContext 绑定:DataContext(或一般父元素)的 DataTemplate 是隐式继承的。无需明确设置。 在您的情况下,所有项目都已加载,因为您的ListBox没有高度限制。它会自动拉伸以使所有项目适合。要启用 UI 虚拟化,请为 ListBox 分配一个 Height,以便 ScrollViewer 可以工作。 ScrollViewer 对于 UI 虚拟化是必不可少的。 高度可以显式设置,例如通过设置 ListBox.Height 属性或隐式设置,例如通过将 ListBox 添加到 Grid,其中行高设置为除 Auto 以外的任何值。 此外,您不应替换源集合。过滤、排序和分组是通过修改“CollectionViewthat is bound to theItemsControl. In WPF the binding engine will automatically use the default ColectionViewto display the items. This allows you to modify the displayed items without modifying the original underlying collection. For example, sorting aCollectionView”完成的,不会对底层集合进行排序。 你用静态的CollectionViewSource.GetDefaultView方法获取默认的CollectionView: <ListBox ItemsSource="{Binding Containers}" VirtualizingPanel.VirtualizationMode="Recycling" Height="300"> <ListBox.ItemTemplate> <DataTemplate DataType="ContainerViewModel"> <ContainerUserControl /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> private ObservableCollection<ContainerViewModel> _containers; public ObservableCollection<ContainerViewModel> Containers { get => _containers; set { _containers = value; OnPropertyChanged(); } } private void SortContainersByName() { var sortDescription = new SortDescription(nameof(ContainerViewModel.Name), ListSortDirection.Ascending); ICollectionView containersView = CollectionViewSource.GetDefaultView(this.Containers); // Apply sorting criteria containersView.SortDescriptions.Add(sortDescription); } private void ClearSortContainersByName() { SortDescription sortDescriptionToRemove = this.DataGridItemsView.SortDescriptions .FirstOrDefault(sortDescription => sortDescription.PropertyName.Equals(nameof(ContainerViewModel.Name), StringComparison.Ordinal)); ICollectionView containersView = CollectionViewSource.GetDefaultView(this.Containers); // Clear sorting criteria containersView.SortDescriptions.Remove(sortDescriptionToRemove); }

回答 1 投票 0

如何智能地创建和调试嵌套堆栈布局的复杂 Maui 数据绑定?

我一直在尝试创建一个数据绑定来访问父堆栈布局中的值。 我创建了一个转换器,认为这可以让我了解我的绑定在做什么。

回答 0 投票 0

.NET MAUI 中的 ObservableCollection 条件颜色绑定

在 .NET MAUI 中使用绑定和 ObservableCollection,如何有条件地设置 GUI 元素的 BackgroundColor 属性并单独评估每个项目的条件? XAML 在 .NET MAUI 中使用绑定和 ObservableCollection,如何有条件地设置 GUI 元素的 BackgroundColor 属性并单独评估每个项目的条件? XAML<Label BackgroundColor="{Binding name.color}"/> C#if (x) { name.color = Colors.Red; } else { name.color = Colors.Blue; } 或者像这样使用字符串数据: C#name.color = "#FF0000"; 不起作用。 C#LabelCommonName.BackgroundColor = Colors.Red; 使用 CollectionView 是不可能的。 我的实际 XAML 我省略了边距、填充、网格定义和其他不相关的样式。 <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:projectName" x:Class="projectName.CalendarPage" Title="CalendarPage"> <Grid> <ScrollView> <RefreshView x:Name="CalendarRefresh" Refreshing="RefreshCalendar"> <CollectionView x:Name="CalendarView"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="local:Calendar"> <Frame> <Grid> <!-- Trying to evaluate this BackgroundColor for each item in the CollectionView individually, so not the same value for all items: --> <Label BackgroundColor="{Binding if (name.color=1) { Colors.Red } else if (name.color=2) { Colors.Blue } else { Colors.Green } }" x:Name="LabelCommonName" Text="{Binding name.common}"/> <Label Text="{Binding name.scientific}"/> </Grid> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </RefreshView> </ScrollView> </Grid> </ContentPage> 我的代码隐藏 向下滚动时使用 ObservableCollection 添加更多项目。 public partial class CalendarPage : ContentPage { private ActivityIndicator activityIndicator = new ActivityIndicator() { IsRunning = false }; private ObservableCollection<Calendar> calendars = new ObservableCollection<Calendar>(); private string url = "[censored]"; private int start = 0; private int limit = 10; public CalendarPage() { InitializeComponent(); LoadCalendar(); } private void RefreshCalendar(object sender, EventArgs e) { LoadCalendar(); CalendarRefresh.IsRefreshing = false; } private void ScrollCalendar(object sender, ItemsViewScrolledEventArgs e) { if (e.LastVisibleItemIndex > (start - limit)) { LoadCalendar(); } } private async void LoadCalendar() { if (activityIndicator.IsRunning == true) { return; } else { activityIndicator.IsRunning = true; var http = new HttpClient(); var items = await http.GetFromJsonAsync<List<Calendar>>(url + start + limit); foreach (var item in items) { calendars.Add(item); } CalendarView.ItemsSource = calendars; activityIndicator.IsRunning = false; start += limit; } } } 日历班 项目是通过外部 API 收集的,因此有一个项目模板。 namespace projectName; public class Calendar { public int item_id { get; set; } public int status { get; set; } public Name name { get; set; } } public class Name { public string common { get; set; } public string scientific { get; set; } public string color { get; set; } } 您可以使用绑定值转换器. 您可以定义一个继承 IValueConverter 的 ColorChangedConverter: public class ColorChangedConverter : IValueConverter { public ColorChangedConverter() { } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if ((int)value == 1) { return Colors.Red; } else if ((int)value == 2) { return Colors.Blue; } else { return Colors.Green; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } 在 xaml 中使用转换器: <ContentPage.Resources> <local:ColorChangedConverter x:Key="colorChangedConverter" /> </ContentPage.Resources> <Grid> <ScrollView> <RefreshView x:Name="CalendarRefresh" > <CollectionView x:Name="CalendarView"> <CollectionView.ItemTemplate> <DataTemplate x:DataType="local:Calendar"> <Frame x:Name="myframe"> <StackLayout> <Label x:Name="LabelCommonName" Text="{Binding name.common}" BackgroundColor="{Binding name.color,Converter={StaticResource colorChangedConverter}}"> </Label> <Label Text="{Binding name.scientific}" TextColor="{Binding name.color}"/> <Label x:Name="mylabel" Text="{Binding item_id}"/> </StackLayout> </Frame> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </RefreshView> </ScrollView> </Grid> 在后面的代码中,我添加了一些测试代码: private ObservableCollection<Calendar> calendars = new ObservableCollection<Calendar>(); public MainPage() { InitializeComponent(); LoadCalendar(); } private async void LoadCalendar() { calendars.Add(new Calendar { item_id = 1, status = 1, name = new Name { common = "common1", scientific = "scientific1", color = 1 } }); calendars.Add(new Calendar { item_id = 2, status = 2, name = new Name { common = "common2", scientific = "scientific2", color = 2 } }); calendars.Add(new Calendar { item_id = 3, status = 3, name = new Name { common = "common3", scientific = "scientific3", color = 3 } }); CalendarView.ItemsSource = calendars; } 下图为结果: 希望它有效。

回答 1 投票 0

在嵌套的 StackLayout 中使用 Maui XAML 绑定 InvHeader/InvItems,其中 InvHeader.CustId = InvItem.CustId?

我正在尝试在 Maui 中创建一个母版/详细信息页面。就像带有发票项目的发票一样,此页面必须显示多个“发票”,每个发票都有自己的发票项目。 假设我们有一个清单......

回答 2 投票 0

如果我最初从数据库设置类的属性,如何实现 INotifyPropertyChanged? [关闭]

我从数据库获取对象到 List,然后我将这个 List 绑定到 Datagrid。当我从数据库中获取数据时,无论如何都会触发 OnPropertyChanged(公共属性的设置器包含 OnPropertyChanged)!...

回答 0 投票 0

Android 绑定 - 获取所有 EditText 值

我有一项活动,其中包含 EditText 字段。使用数据绑定,所以我可以像这样从 EditText 获取字符串值: 绑定.printWidth.getText().toString() 有没有办法让所有的 id 和值 f...

回答 0 投票 0

为元素指定 DataContext 使用 AvaloniaUI 重置其绑定值

我已经将不同用户控件中某些文本框的值绑定到视图模型中的某些值,仅来自其中一个。在一个名为“PreferencesView&q...

回答 0 投票 0

CommunityToolkit.Mvvm:更改源生成的属性设置器行为

正如主题所说。是否可以在源生成的 setter 方法中“注入”一些空检查? 我正在使用 CommunityToolkit.Mvvm 8.1.0.0 对于这个演示,我有简单的 class Test with private f...

回答 1 投票 0

.NET MAUI 绑定到 GraphicsView

我无法理解绑定在 .NET MAUI 中的工作方式。我的目标是有两种方式在 CollectionView 中显示分数,一个带有数字的标签和一个图形表示。

回答 1 投票 0

在 UWP 中为 ListView 数据模板处理数组项

我有以下 DataTemplate,用于我的 UWP 应用程序中的自定义 ListView 布局: 我有以下 DataTemplate,我在我的 UWP 应用程序中用于自定义 ListView 布局: <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)" xmlns:utils="using:ClassevivaPCTO.Utils" x:Class="ClassevivaPCTO.Controls.DataTemplates.AgendaEventListViewDataTemplate" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> <utils:AgendaEvent x:Key="AgendaEvent"/> <DataTemplate x:Key="AgendaEventListViewDataTemplate" x:DataType="utils:AgendaEvent"> <RelativePanel> <TextBlock x:Name="eventTitle" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind subjectDesc}" Style="{ThemeResource BaseTextBlockStyle}" Margin="12,6,0,0" /> <TextBlock RelativePanel.RightOf="eventTitle" x:Name="eventType" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind evtCode}" Style="{ThemeResource BaseTextBlockStyle}" Margin="12,6,0,0" /> </RelativePanel> </DataTemplate> </ResourceDictionary> TextBlock 项的 Text 值绑定到名为 AgendaEvent 的自定义数据类型类: public class AgendaEvent { public int evtId { get; set; } public string evtCode { get; set; } public DateTime evtDatetimeBegin { get; set; } public DateTime evtDatetimeEnd { get; set; } public bool isFullDay { get; set; } public string notes { get; set; } public string authorName { get; set; } public string classDesc { get; set; } public object subjectId { get; set; } public object subjectDesc { get; set; } public object homeworkId { get; set; } } 这是我的列表视图: <ListView x:Name="ListViewAgendaEvents" SelectionMode="Single" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemTemplate="{StaticResource AgendaEventListViewDataTemplate}" ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled"> </ListView> 从我从自定义数组工厂获得的AgendaEvent对象数组中,我将ItemsSource设置为ListView: ListViewAgendaEvents.ItemsSource = customArray.AgendaEvents; 我意识到,根据我的需要,eventTitle TextBlock 需要将属性 authorName 和 subjectDesc 组合在一起作为带有自定义装饰字符的字符串。例如:authorName + " - wrote: " + subjectDesc. 并不总是 subjectDesc 可用,所以我需要通过各种条件检查来动态调整 eventTitle 文本块。 我考虑过使用适配器类,就像 Android 的列表视图一样,但是使用 UWP 的数据绑定,如何完成我想做的事情是非常混乱的。 是否有一种简单直接的方法来组合自定义字符串中的属性并在显示 UWP 中的 ListView 中使用的 DataTemplate 的属性之前执行检查? 更新3 UWP 不支持多个数据绑定,它只接受单个值。如果您不想更改 Xaml 绑定,请尝试在 DataModel 中进行。在 AgendaEvent 类中创建一个新的消息字符串并进行检查将是最简单的方法。 public class AgendaEvent { public int evtId { get; set; } public string evtCode { get; set; } public DateTime evtDatetimeBegin { get; set; } public DateTime evtDatetimeEnd { get; set; } public bool isFullDay { get; set; } public string notes { get; set; } public string authorName { get; set; } public string classDesc { get; set; } public object subjectId { get; set; } public object subjectDesc { get; set; } public object homeworkId { get; set; } public string messageString { get { if (subjectDesc == null) { return authorName; } return authorName + " - wrote: " + subjectDesc; } } } Xaml: <TextBlock x:Name="eventTitle" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind messageString}" Style="{ThemeResource BaseTextBlockStyle}" Margin="12,6,0,0" /> 更新2: 我想出了另一种方法,应该也能达到你想要的效果。它需要不同的模板。您只需要根据使用 DataTemplateSelector Class 的值选择不同的DataTemplate 代码隐藏: public class MyDataTemplateSelector : DataTemplateSelector { public DataTemplate Normal { get; set; } public DataTemplate Unusual { get; set; } protected override DataTemplate SelectTemplateCore(object item) { AgendaEvent agendaEvent = (AgendaEvent)item; // if you have other reuqirements, just need to add more check and different templates if (agendaEvent.subjectDesc != null) { return Normal; } else { return Unusual; } } } Xaml: <Page.Resources> <!--tempalte for normal value--> <DataTemplate x:Key="NormalItemTemplate" x:DataType="local:AgendaEvent"> <RelativePanel> <TextBlock x:Name="eventTitle" TextWrapping="Wrap" MaxWidth="500" Margin="12,6,0,0"> <Run Text="{x:Bind authorName}"/> <Run Text=" - wrote: "/> <Run Text="{x:Bind subjectDesc}"/> </TextBlock> <TextBlock RelativePanel.RightOf="eventTitle" x:Name="eventType" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind evtCode}" Margin="12,6,0,0" /> </RelativePanel> </DataTemplate> <!--template for null value--> <DataTemplate x:Key="UnusualItemTemplate" x:DataType="local:AgendaEvent"> <RelativePanel> <TextBlock x:Name="eventTitle" TextWrapping="Wrap" MaxWidth="500" Margin="12,6,0,0"> <Run Text="{x:Bind authorName}"/> </TextBlock> <TextBlock RelativePanel.RightOf="eventTitle" x:Name="eventType" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind evtCode}" Margin="12,6,0,0" /> </RelativePanel> </DataTemplate> <local:MyDataTemplateSelector x:Key="MyDataTemplateSelector" Normal="{StaticResource NormalItemTemplate}" Unusual="{StaticResource UnusualItemTemplate}"/> </Page.Resources> <Grid> <ListView x:Name="ListViewAgendaEvents" SelectionMode="Single" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemTemplateSelector="{StaticResource MyDataTemplateSelector}" ScrollViewer.VerticalScrollMode="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled"> </ListView> </Grid> 更新: 您可以尝试创建自定义 ValueConverter 来完成这项工作。您可以检查 Converter 事件中的绑定值并自定义输出绑定字符串。 代码隐藏: public class TextBlockValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, string language) { if (value == null) { return ""; } else { return " - wrote: " + value.ToString(); } } public object ConvertBack(object value, Type targetType, object parameter, string language) { throw new NotImplementedException(); } } Xaml: <Page.Resources> <local:TextBlockValueConverter x:Key="TextBlockValueConverter"/> </Page.Resources> <TextBlock x:Name="eventTitle" TextWrapping="Wrap" MaxWidth="500" Margin="12,6,0,0"> <Run Text="{x:Bind authorName}"/> <Run Text="{x:Bind subjectDesc, Converter={StaticResource TextBlockValueConverter} }"/> </TextBlock> 老 对于 UWP 中的 ListView 中使用的 DataTemplate,是否有一种简单直接的方法来组合自定义字符串中的属性? 既然你用的是TextBlock,那么解决方法就很简单了。您只需要在 Run 中使用 TextBlock。并将你想要的文字绑定到不同的Run. 你只需要像这样更改代码: <TextBlock x:Name="eventTitle" TextWrapping="Wrap" MaxWidth="500" Margin="12,6,0,0"> <Run Text="{x:Bind authorName}"/> <Run Text=" - wrote: "/> <Run Text="{x:Bind subjectDesc}"/> </TextBlock> 结果看起来像: 根据 Roy Li 的回答,我使用了以下最适合我的需求并且部分类似于 Android 的适配器架构的方法: 这是一个单独的类,充当 xaml 列表视图项的“适配器”或“视图模型”。在构造函数中,它采用具有我要处理的属性的原始对象类型: public class AgendaEventAdapter { public AgendaEvent CurrentObject; public string Title { get { if (string.IsNullOrEmpty(CurrentObject.subjectDesc)) { return CurrentObject.authorName; } else { return CurrentObject.subjectDesc + " (" + CurrentObject.authorName + ")"; } } } public string EventType { get { return "[" + CurrentObject.evtCode + "]"; } } public string Notes => CurrentObject.notes; public AgendaEventAdapter(AgendaEvent ev) { CurrentObject = ev; } } 这是listview中使用的DataTemplate(注意DataType现在是适配器类AgendaEventAdapter): <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:contract7Present="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract, 7)" xmlns:converters="using:ClassevivaPCTO.Converters" xmlns:utils="using:ClassevivaPCTO.Utils" xmlns:adapters="using:ClassevivaPCTO.Adapters" x:Class="ClassevivaPCTO.Controls.DataTemplates.AgendaEventListViewDataTemplate" xmlns:muxc="using:Microsoft.UI.Xaml.Controls"> <converters:DateTimeToHourConverter x:Key="DateTimeToHourConverter" /> <DataTemplate x:Key="AgendaEventListViewDataTemplate" x:DataType="adapters:AgendaEventAdapter"> <RelativePanel> <TextBlock x:Name="titoloEvento" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind Title}" Style="{ThemeResource BaseTextBlockStyle}" Margin="6,6,0,0" /> <TextBlock RelativePanel.RightOf="titoloEvento" x:Name="tipoEvento" TextWrapping="Wrap" MaxWidth="500" Text="{x:Bind EventType}" Style="{ThemeResource BaseTextBlockStyle}" Margin="6,6,0,0" /> <TextBlock RelativePanel.Below="titoloEvento" x:Name="dataStart" Text="{x:Bind CurrentObject.evtDatetimeBegin, Converter={StaticResource DateTimeToHourConverter}}" FontStyle="Italic" Style="{ThemeResource BodyTextBlockStyle}" Margin="6,0,0,0" /> <TextBlock RelativePanel.RightOf="dataStart" RelativePanel.Below="titoloEvento" x:Name="placeholderData" Text="-" FontStyle="Italic" Style="{ThemeResource BodyTextBlockStyle}" Margin="6,0,6,0" /> <TextBlock RelativePanel.RightOf="placeholderData" RelativePanel.Below="titoloEvento" x:Name="dataEnd" Text="{x:Bind CurrentObject.evtDatetimeEnd, Converter={StaticResource DateTimeToHourConverter}}" FontStyle="Italic" Style="{ThemeResource BodyTextBlockStyle}" Margin="0,0,0,0" /> <TextBlock RelativePanel.Below="dataStart" x:Name="descriptionEvent" TextWrapping="Wrap" Text="{x:Bind CurrentObject.notes}" Style="{ThemeResource BodyTextBlockStyle}" Margin="6,6,0,6" /> </RelativePanel> </DataTemplate> </ResourceDictionary> 这是为每个项目创建适配器类实例的代码,然后将其传递给列表视图的ItemsSource属性: // Wrap each AgendaEvent object in an instance of AgendaEventAdapter and handle null case var eventAdapters = overviewResult.AgendaEvents ?.Select(evt => new AgendaEventAdapter(evt)) .ToList(); ListViewAgendaDate.ItemsSource = eventAdapters; 这是我的问题的最佳解决方案,因为拥有一个单独的模型类有助于减少代码中的混乱,并且在使用与项目视图绑定的可观察对象的情况下也很有用。

回答 2 投票 0

类型'字符串|编号 |尝试绑定到 dx-date-box

我正在尝试使用两种方式绑定到组件打字稿的 Datebox。在此示例中,它似乎有效,但我无法说出我遗漏了什么可以使我的工作正常进行。 HTML 片段: &...

回答 0 投票 0

当模式设置为 OneWay 时绑定对象被清除 - 使用 TwoWay 工作正常

我有一个简单的 wpf 弹出控件。当用户在文本框中输入错误的年龄时,我想显示弹出窗口。 我的代码片段 我有一个简单的 wpf Popup 控件。当用户在文本框中输入错误的年龄时,我想显示弹出窗口。 我的代码片段 <TextBox Name="TxtAge" LostFocus="TxtAge_OnLostFocus" ></TextBox> <Popup Name="InvalidAgePopup" IsOpen="{Binding IsAgeInvalid, Mode=OneWay}"/> 代码隐藏 private void TxtAge_OnLostFocus(object sender, RoutedEventArgs e) { var text = TxtAge.Text; double value = 0; if (Double.TryParse(text, out value)) { vm.IsAgeInvalid = false; } else { vm.IsAgeInvalid = true; } } 视图模型 public class AgeViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } private bool _isAgeInvalid; public bool IsAgeInvalid { get { return _isAgeInvalid; } set { _isAgeInvalid = value; OnPropertyChanged(); } } } IsAgeInvalid 属性在输入无效年龄的文本框时设置。那时我想显示弹出窗口。而且我不想在弹出控件关闭时设置 IsAgeInvalid = false。 为此,我设置 Mode=OneWay IsOpen="{Binding IsAgeInvalid, Mode=OneWay} 当我输入错误的数据时,弹出窗口显示正常。当我关闭弹出窗口时,我的绑定对象正在被清除。 以下截图来自snoop工具。 绑定对象第一次出现。 弹窗关闭后,绑定对象清空。 绑定部分在 TwoWay 模式下工作正常,我不希望将 IsAgeInvalid 属性设置为 false,因为 IsOpen 设置为 false。 我试过设置 UpdateSourceTriger 和其他几种方法,弹出窗口关闭后绑定对象仍然被清除。 假设发生的事情是 WPF 错误或只是一个不值得失眠的怪癖,这里有一个(可能是显而易见的)解决方法: 视图模型: private bool _isAgeInvalid; public bool IsAgeInvalid { get { return _isAgeInvalid; } set { _isAgeInvalid = value; this.IsAgeValidationPopupOpen = valid; OnPropertyChanged(); } } private bool _isAgeValidationPopupOpen; public bool IsAgeValidationPopupOpen { get => _isAgeValidationPopupOpen; set { _isAgeValidationPopupOpen = value; OnPropertyChanged(); } } XAML: <Popup Name="InvalidAgePopup" IsOpen="{Binding IsAgeValidationPopupOpen, Mode=TwoWay}"/> 即使弹出窗口的关闭不应该破坏你的绑定,至少使用这种方法你有一个变量总是跟踪弹出窗口打开状态,另一个变量总是跟踪年龄有效性状态,所以可以说它是你的 UI 状态的更好表示. 你的代码有一些问题。您没有理由不将 TextBox 绑定到您的视图模型类。验证视图中的输入只是为了在视图模型上设置一个属性以指示验证失败是没有意义的。这样的属性必须是只读的,以确保不能随意设置。如果您选择在视图的代码隐藏中进行验证,那么只需从那里切换Popup。 但是,我建议实施INotifyDataErrorInfo(见下文)。 解释你的观察 根据您的陈述 “当我关闭弹出窗口时,我的绑定对象正在被清除。” 和 “我不希望 IsAgeInvalid 属性设置为 false,因为 IsOpen 设置为 false” 我得出结论,您不会通过将Popup设置为AgeViewModel.IsAgeInvalid来关闭false。相反,您直接设置 Popup.IsOpen 以关闭 Popup. 重要的一点是,如果 Binding.Mode 设置为 BindingMode.OneWay 并且直接(本地)设置依赖属性,则新值将清除/覆盖以前的值和值源,在您的情况下是一个 Binding 对象定义了 IsAgeInvalid 作为源属性。 这就是为什么您在关闭Binding(您的方式)时观察到Popup被删除的原因。 在本地设置依赖属性将始终清除以前的值和值源(Binding 和本地值具有相同的优先级)。 因为IsOpen绑定到IsAgeInvalid,你必须通过绑定来设置它(这是你明确不想做的)。 解决方案 1 使用Popup.IsOpen方法设置DependencyObject.SetCurrentValue属性。 这允许在不清除值源的情况下在本地分配新值(Binding): this.Popup.SetCurrentValue(Popup.IsOpenProperty, false); 方案二(推荐) 另外解决设计问题的正确解决方案是: 让您的视图模型类 AgeViewModel 实现 INotifyDataErrorInfo 以启用属性验证。 覆盖默认的验证错误反馈。当验证失败时,WPF 将自动在绑定目标周围绘制一个红色边框(在您的例子中为TextBox)。 您可以通过定义分配给附加的 ControlTemplate 属性的 Validation.ErrorTemplate 来非常轻松地自定义错误反馈。 以下示例使用解决方案 2) “使用 lambda 表达式和委托进行数据验证” 来自 如何添加验证以查看模型属性或如何实现 INotifyDataErrorInfo。只有属性 UserInput 被重命名为 Age 并且方法 IsUserInputValid 被重命名为 IsAgevalid 以使其解决您给定的场景。 AgeViewModel.cs class AgeViewModel : INotifyPropertyChanged, INotifyDataErrorInfo { // This property is validated using a lambda expression private string age; public string Age { get => this.age; set { // Use Method Group if (IsPropertyValid(value, IsAgeValid)) { this.age = value; OnPropertyChanged(); } } } // Constructor public AgeViewModel() { this.Errors = new Dictionary<string, IList<object>>(); } // The validation method for the UserInput property private (bool IsValid, IEnumerable<object> ErrorMessages) IsAgeValid(string value) { return double.TryParse(value, out _) ? (true, Enumerable.Empty<object>()) : (false, new[] { "Age must be numeric." }); } /***** Implementation of INotifyDataErrorInfo *****/ // Example uses System.ValueTuple public bool IsPropertyValid<TValue>( TValue value, Func<TValue, (bool IsValid, IEnumerable<object> ErrorMessages)> validationDelegate, [CallerMemberName] string propertyName = null) { // Clear previous errors of the current property to be validated _ = ClearErrors(propertyName); // Validate using the delegate (bool IsValid, IEnumerable<object> ErrorMessages) validationResult = validationDelegate?.Invoke(value) ?? (true, Enumerable.Empty<object>()); if (!validationResult.IsValid) { AddErrorRange(propertyName, validationResult.ErrorMessages); } return validationResult.IsValid; } // Adds the specified errors to the errors collection if it is not // already present, inserting it in the first position if 'isWarning' is // false. Raises the ErrorsChanged event if the Errors collection changes. // A property can have multiple errors. private void AddErrorRange(string propertyName, IEnumerable<object> newErrors, bool isWarning = false) { if (!newErrors.Any()) { return; } if (!this.Errors.TryGetValue(propertyName, out IList<object> propertyErrors)) { propertyErrors = new List<object>(); this.Errors.Add(propertyName, propertyErrors); } if (isWarning) { foreach (object error in newErrors) { propertyErrors.Add(error); } } else { foreach (object error in newErrors) { propertyErrors.Insert(0, error); } } OnErrorsChanged(propertyName); } // Removes all errors of the specified property. // Raises the ErrorsChanged event if the Errors collection changes. public bool ClearErrors(string propertyName) { this.ValidatedAttributedProperties.Remove(propertyName); if (this.Errors.Remove(propertyName)) { OnErrorsChanged(propertyName); return true; } return false; } // Optional method to check if a particular property has validation errors public bool PropertyHasErrors(string propertyName) => this.Errors.TryGetValue(propertyName, out IList<object> propertyErrors) && propertyErrors.Any(); #region INotifyDataErrorInfo implementation // The WPF binding engine will listen to this event public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged; // This implementation of GetErrors returns all errors of the specified property. // If the argument is 'null' instead of the property's name, // then the method will return all errors of all properties. // This method is called by the WPF binding engine when ErrorsChanged event was raised and HasErrors return true public System.Collections.IEnumerable GetErrors(string propertyName) => string.IsNullOrWhiteSpace(propertyName) ? this.Errors.SelectMany(entry => entry.Value) : this.Errors.TryGetValue(propertyName, out IList<object> errors) ? (IEnumerable<object>)errors : new List<object>(); // Returns 'true' if the view model has any invalid property public bool HasErrors => this.Errors.Any(); #endregion #region INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; #endregion protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected virtual void OnErrorsChanged(string propertyName) { this.ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName)); } // Maps a property name to a list of errors that belong to this property private Dictionary<string, IList<object>> Errors { get; } } MainWindow.xaml AgeViewModel 中生成的错误消息由绑定引擎自动包装到 ValidationError 对象中。 我们可以通过引用 ValidationError.ErrorContent 属性来获取消息。 <Window> <Window.Resources> <ControlTemplate x:Key="ValidationErrorTemplate"> <StackPanel> <Border BorderBrush="Red" BorderThickness="1" HorizontalAlignment="Left"> <!-- Placeholder for the TextBox itself --> <AdornedElementPlaceholder x:Name="AdornedElement" /> </Border> <!-- Your Popup goes here. The Popup will close automatically once the error template is disabled by the WPF binding engine. Because this content of the ControlTemplate is already part of a Popup, you could skip your Popup and only add the TextBlock or whatever you want to customize the look --> <Popup IsOpen="True"> <!-- The error message from the view model is automatically wrapped into a System.Windows.Controls.ValidationError object. Our view moel returns a collection of messages. So we bind to the first message --> <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red" /> </Popup> </StackPanel> </ControlTemplate> </Window.Resources> <!-- Enable validation and override the error template using the attached property 'Validation.ErrorTemplate' --> <TextBox Text="{Binding Age, ValidatesOnNotifyDataErrors=True}" Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}" /> </Window>

回答 2 投票 0

WPF画布显示像Word文本一样的弹出命令控件

在我的应用程序中,我允许用户选择添加到画布中的形状。当用户在画布中选择特定形状时,我想在所选形状的顶部显示用户控件。用户

回答 0 投票 0

当 Combobox Itemsource 绑定到 WPF 中的 List/ObservableCollection 时,如何在运行时更新/刷新 Combobox Items/内容?

我有一个使用 ComboBox 的 WPF 应用程序。我的 ComboBox ItemSource 绑定到我在后台从 C# 填充的列表 <>。 这是 List<> 的 C# 代码: 公开部分...

回答 1 投票 0

如何使用多个选项卡导入和绑定多个 excel(名称常见)

我想导入多个excel文件。每个文件包含 3 个与其他文件同名的选项卡。例如。学生姓名,学生分部,学生成绩。 我想导入所有文件并将它们绑定到唯一的...

回答 1 投票 0

在 Xamarin 绑定表达式中格式化文本

我意识到我可能没有在主题标题中完全解释它,而我真正想做的是; 我知道我可能没有在主题标题中完全解释它,而我真正想做的是; <Label Text="Order Remaining :" Grid.Row="2" Grid.Column="0"></Label> <Label Text="{Binding RemainingAmount}" Grid.Row="2" Grid.Column="1"></Label> 我想在上面提到的代码中遇到的是: 剩余订单:120 数量 正如你在这里看到的,我不能添加“数量”这个词,我可以在不创建另一个标签的情况下做像{Binding RemaininAmount + 'Amount'}这样的事情吗? 也许像这样,将 Grid.Row 和 Column 添加到 Label <Label> <Label.FormattedText> <FormattedString> <Span Text="Order Remaining :"/> <Span Text=" "/> <Span Text="{Binding RemainingAmount}"/> <Span Text=" "/> <Span Text="amount"/> </FormattedString> </Label.FormattedText> </Label> https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/multibinding?WT.mc_id=DOP-MVP-5002948#format-strings 您可以像这样在绑定表达式中使用StringFormat: <Label Text="Order Remaining :" Grid.Row="2" Grid.Column="0" /> <Label Text="{Binding RemainingAmount, StringFormat='{0:d} amount'}" Grid.Row="2" Grid.Column="1" />

回答 2 投票 0

如何使用挖空显示图片和文字?

我正在学习更多关于 knockout.js 的知识。我想将图像添加到我的文本数据中,并根据数组中提供的图像链接及其匹配数据显示正确的图像。我只想要一套......

回答 1 投票 0

MAUI 只绑定一个字符串

我在 Maui 中绑定一个简单的字符串时遇到了很多麻烦,我遇到的所有示例都使用 ViewModel 和 ViewModel 中的命令来处理字符串并绑定信息

回答 0 投票 0

Spring mvc 中提交表单的 ArrayList 数据绑定

我对 spring mvc 很陌生,有下面的代码 模范国家 公共课国家{ 私有 int id; 私有字符串名称; @getter 和 setter 模型账户 公共课帐户{ p...

回答 0 投票 0

如何在网站第一次加载时调用函数?

我正在尝试在 vue 中绑定图像 :src="require(`../image/animal_image/${featuredData[3].image_name}`)" 但是,当单击该函数时此方法有效,因为数据来自后端。 我...

回答 1 投票 0

© www.soinside.com 2019 - 2024. All rights reserved.