XAML WinUI 3绑定到组合框的奇怪问题,selectedItem始终为空

bmvo0sr5  于 2023-09-28  发布在  其他
关注(0)|答案(2)|浏览(99)

我正在使用WinUI 3创建应用程序。我有一个奇怪的绑定问题。我做了几次同样的事情,这就是为什么我似乎不能弄清楚为什么它在这个特定的情况下不起作用。
这是导致问题的代码段:

<ComboBox Header="Base view"  
          HorizontalAlignment="Stretch"
           IsEnabled="{x:Bind ViewModel.SelectedViewConfiguration.BaseView, Converter={StaticResource IsNotNullConverter}, Mode=OneWay}"
           ItemsSource="{x:Bind ViewModel.OtherViewConfigurations, Mode=OneWay}" 
           SelectedItem="{x:Bind ViewModel.SelectedViewConfiguration.BaseView,  Mode=OneWay}">

    <ComboBox.ItemTemplate>
        <DataTemplate x:DataType="viewModels:ViewConfigurationViewModel">
            <TextBlock>
                View <Run Text="{x:Bind Number}" />
            </TextBlock>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

应用程序的此部分允许用户自定义图纸上视图的位置和选项。“视图”是画布上的按钮。我附上了一个截图。在按钮上单击“我想查看右侧的一些属性”,除了“基本视图”属性外,其他属性都可以正常工作。
app views
SelectedItem上的模式应为TwoWay。我现在将其设置为OneWay,因为我注意到在单击按钮并执行命令后,BaseView属性被设置为null。
即使将其设置为单向,也不会显示该属性。但是,当我按下下拉菜单时,我确实看到了选项。
view dropdown
我的视图模型中的BaseView属性是一个ObservableProperty。目前我正在使用OnBaseViewChanged事件设置模型属性。

[ObservableProperty]
private ViewConfigurationViewModel? baseView;

partial void OnBaseViewChanged(ViewConfigurationViewModel? value)
{
   ViewConfiguration.BaseView = value?.ViewConfiguration;        
}

这是我的按钮点击命令处理程序

[RelayCommand]
private void ViewConfigurationClicked(ViewConfigurationViewModel item)
{
    SelectedViewConfiguration = item;
}

当我查看可视树时,我可以看到SelectedItem为0。对于其他组合框,将按预期加载值。
有人知道是什么导致的吗?如果有必要,我可以提供更多的代码。
更新-更多的上下文,我已经删除了一些无关的属性。
页面视图模型:
public partial class DrawingsViewModel:ObservableRecipient { private readonly IDrawingService drawingService;

public DrawingsViewModel(IDrawingService drawingService)
{
    this.drawingService = drawingService;

    drawingConfigurations = new ObservableCollection<DrawingConfiguration>(drawingService.GetDrawingConfigurations());

    SelectedDrawingConfiguration = drawingConfigurations.First();
}

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(OtherViewConfigurations))]
private ViewConfigurationViewModel? selectedViewConfiguration;

[ObservableProperty] 
private ObservableCollection<ViewConfigurationViewModel>? viewConfigurations;
public List<ViewConfigurationViewModel>? OtherViewConfigurations => ViewConfigurations?.Where(x => x != SelectedViewConfiguration).ToList();

[ObservableProperty] 
private ObservableCollection<DrawingConfiguration> drawingConfigurations;

[ObservableProperty] 
private DrawingConfiguration selectedDrawingConfiguration;

partial void OnSelectedDrawingConfigurationChanged(DrawingConfiguration value)
{
    ViewConfigurations = GetViews(SelectedDrawingConfiguration);

    // reset selected view when switching 
    SelectedViewConfiguration = null;
}

public ObservableCollection<ViewConfigurationViewModel> GetViews(DrawingConfiguration config)
{
    var vms = new List<ViewConfigurationViewModel>(config.Views
        .Select(x => new ViewConfigurationViewModel(x, config.SheetSize)));

    // go through the created viewmodels and assign the base view(model)
    foreach (var vm in vms)
    {
        if (vm.ViewConfiguration.BaseView == null) { continue; }

        var otherVms = vms.Where(x => x != vm).ToList();

        vm.BaseView = otherVms.FirstOrDefault(x => x.ViewConfiguration.Number == vm.ViewConfiguration.BaseView.Number);
    }

    return new ObservableCollection<ViewConfigurationViewModel>(vms);
}

// TODO: I should find a way to use a Selector or Listbox as container for the canvasview
[RelayCommand]
private void ViewConfigurationClicked(ViewConfigurationViewModel item)
{
    // item.BaseView is not null here
    SelectedViewConfiguration = item;
}

}
我用来表示‘视图’的视图模型--我还没有为此制作一个单独的视图,因为我需要控制画布上项目的位置,但现在这已经无关紧要了。

public partial class ViewConfigurationViewModel : ObservableObject
{
    public readonly ViewConfiguration ViewConfiguration;
    private readonly SheetSize sheetSize;

    public int Number => ViewConfiguration.Number;

    public ViewConfigurationViewModel(ViewConfiguration viewConfiguration, SheetSize sheetSize)
    {
        this.ViewConfiguration = viewConfiguration;
    }
     
    public ViewType ViewType
    {
        get => ViewConfiguration.ViewType;
        set =>
            SetProperty(ViewConfiguration.ViewType, value,
                ViewConfiguration, (u, n) => u.ViewType = n);
    }

    [ObservableProperty]
    private ViewConfigurationViewModel? baseView;

    partial void OnBaseViewChanged(ViewConfigurationViewModel? value)
    {
        if (value == null)
        {
            return;
        }

        if (ViewConfiguration.BaseView != value?.ViewConfiguration) 
            ViewConfiguration.BaseView = value?.ViewConfiguration;
    }

}

这是XAML,我再次删除了一些东西:

<!--left-->
            <controls1:CanvasView Height="600" Width="900" HorizontalAlignment="Left" 
                                      ItemsSource="{x:Bind ViewModel.ViewConfigurations, Mode=OneWay}">

                <controls1:CanvasView.ContextFlyout>
                    <MenuFlyout>
                        <MenuFlyoutItem Text="Add view" Command="{x:Bind ViewModel.AddViewCommand}" />
                    </MenuFlyout>
                </controls1:CanvasView.ContextFlyout>

                <controls1:CanvasView.ItemTemplate>
                    <DataTemplate x:DataType="viewModels:ViewConfigurationViewModel">

                        <!-- view config view -->
                        <Button Canvas.Left="{x:Bind Left, Mode=TwoWay}"
                                Canvas.Top="{x:Bind Top, Mode=TwoWay}"
                                Background ="{ThemeResource  CardBackgroundFillColorSecondaryBrush}"
                                BorderBrush ="{ThemeResource CardStrokeColorDefaultBrush}"
                                RequestedTheme="Light"
                                Padding="2"
                                BorderThickness="1"
                                Width="180"
                                Height="110"
                                CornerRadius="4"
                                ManipulationMode="TranslateX,TranslateY"
                                Command="{Binding ElementName=drawingPage, Path=ViewModel.ViewConfigurationClickedCommand}"
                                CommandParameter="{x:Bind}">

                            <Button.RenderTransform>
                                <TranslateTransform X="-90" Y="-55"></TranslateTransform>
                            </Button.RenderTransform>

                            <StackPanel>

                                <TextBlock FontWeight="SemiBold" FontSize="14" HorizontalTextAlignment="Center" Margin="0,0,0,10">
                                        View <Run Text="{x:Bind ViewConfiguration.Number}" />
                                </TextBlock>

                                <Grid Padding="3" ColumnSpacing="5">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="*"/>
                                        <ColumnDefinition Width="*"></ColumnDefinition>
                                    </Grid.ColumnDefinitions>
                                    <NumberBox Header="X" FontSize="10" Text="{x:Bind X, Mode=OneWay}"></NumberBox>
                                    <NumberBox Grid.Column="1" Header="Y" FontSize="10" Text="{x:Bind Y, Mode=OneWay}"></NumberBox>
                                </Grid>

                            </StackPanel>
                        </Button>
                    </DataTemplate>
                </controls1:CanvasView.ItemTemplate>

            </controls1:CanvasView>

            <!--right-->
            <StackPanel Grid.Column="1" Spacing="10">

                <Expander HorizontalAlignment="Stretch" 
                          Margin="10,0,0,0"
                          IsExpanded="True"
                          Height="600"
                          HorizontalContentAlignment="Stretch"
                          VerticalContentAlignment="Stretch"
                          VerticalAlignment="Top"
                          Background="{ThemeResource CardBackgroundFillColorSecondaryBrush}">

                    <Expander.Header>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock FontWeight="SemiBold">
                                View <Run Text="{x:Bind ViewModel.SelectedViewConfiguration.Number, Mode=OneWay, FallbackValue=''}"/>
                            </TextBlock>
                        </StackPanel>
                    </Expander.Header>

                    <StackPanel VerticalAlignment="Stretch">

                        <Grid Visibility="{x:Bind ViewModel.SelectedViewConfiguration, Converter={StaticResource IsNotNullToVisibilityConverter}, Mode=OneWay}">
                            <TextBlock Foreground="{ThemeResource SystemColorGrayTextColor}" HorizontalAlignment="Center"
                                       Text="No view selected">
                            </TextBlock>
                        </Grid>

                        <StackPanel Visibility="{x:Bind ViewModel.SelectedViewConfiguration, Converter={StaticResource IsNullToVisibilityConverter}, Mode=OneWay}" Spacing="5">

                            <ComboBox Header="View type" 
                                      HorizontalAlignment="Stretch"
                                      ItemsSource="{x:Bind ViewModel.ViewTypes}"
                                      SelectedItem="{x:Bind ViewModel.SelectedViewConfiguration.ViewType, Mode=TwoWay}">

                                <ComboBox.ItemTemplate>
                                    <DataTemplate x:DataType="config:ViewType">
                                        <TextBlock Text="{Binding Converter={StaticResource EnumToDisplayNameConverter}}"></TextBlock>
                                    </DataTemplate>
                                </ComboBox.ItemTemplate>
                            </ComboBox>

                            <ComboBox Header="Base view"  
                                      HorizontalAlignment="Stretch"
                                       IsEnabled="{x:Bind ViewModel.SelectedViewConfiguration.BaseView, Converter={StaticResource IsNotNullConverter}, Mode=OneWay}"
                                      SelectedItem="{x:Bind ViewModel.SelectedViewConfiguration.BaseView}">

                                <!-- TODO: what's wrong with this box - does not want to select anything --> 

                                <ComboBox.ItemTemplate>
                                    <DataTemplate x:DataType="viewModels:ViewConfigurationViewModel">
                                        <TextBlock>
                                            View <Run Text="{x:Bind Number}" />
                                        </TextBlock>
                                    </DataTemplate>
                                </ComboBox.ItemTemplate>
                            </ComboBox>

                        </StackPanel>
                    </StackPanel>
                </Expander>
            </StackPanel>
        </Grid>

我不确定我在drawing类中创建ViewConfiguration ViewModels的方法,但基本上我有ViewConfiguration对象,它们可以有一个BaseView,这是另一个ViewConfiguration。在页面上,我选择了DrawingConfigurations,它会触发从那里加载视图和其他设置。
更新:
测试仓库,所有内容都在主页面上:
https://github.com/Basnederveen/BindingFailure

eyh26e7m

eyh26e7m1#

我想你的问题是这条线:

SelectedItem="{x:Bind ViewModel.SelectedViewConfiguration.BaseView,  Mode=OneWay}"

SelectedItem需要与ItemsSource的类型相同。
下面是这个答案的最小示例代码。

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Xaml.Controls;
using System.Collections.ObjectModel;

namespace ComboBoxTest;

public class Configuration
{
    public int Number { get; set; }
}

public partial class ConfigurationViewModel : ObservableObject
{
    [ObservableProperty]
    private Configuration _configuration = new();
}

public partial class MainPageViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<ConfigurationViewModel> _configurations = new()
    {
        new ConfigurationViewModel { Configuration = new Configuration { Number = 1 } },
        new ConfigurationViewModel { Configuration = new Configuration { Number = 2 } },
        new ConfigurationViewModel { Configuration = new Configuration { Number = 3 } },
        new ConfigurationViewModel { Configuration = new Configuration { Number = 4 } },
    };

    [ObservableProperty]
    private ConfigurationViewModel? _selectedConfiguration;

    [RelayCommand]
    public void SelectConfiguration(ConfigurationViewModel item)
    {
        SelectedConfiguration = item;
    }
}

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    public MainPageViewModel  ViewModel { get; } = new();
}
<Page
    x:Class="ComboBoxTest.MainPage"
    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:local="using:ComboBoxTest"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    x:Name="ThisPage"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid ColumnDefinitions="*,*">
        <ItemsControl ItemsSource="{x:Bind ViewModel.Configurations, Mode=OneWay}">
            <ItemsControl.ItemTemplate>
                <DataTemplate x:DataType="local:ConfigurationViewModel">
                    <Button
                        Command="{Binding ElementName=ThisPage, Path=ViewModel.SelectConfigurationCommand}"
                        CommandParameter="{x:Bind}"
                        Content="{x:Bind Configuration.Number, Mode=OneWay}" />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

        <ComboBox
            Grid.Column="1"
            Header="Base view"
            ItemsSource="{x:Bind ViewModel.Configurations, Mode=OneWay}"
            SelectedItem="{x:Bind ViewModel.SelectedConfiguration, Mode=OneWay}">
            <ComboBox.ItemTemplate>
                <DataTemplate x:DataType="local:ConfigurationViewModel">
                    <TextBlock Text="{x:Bind Configuration.Number, Mode=OneWay}" />
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </Grid>
</Page>
agxfikkp

agxfikkp2#

我不知道为什么,但问题已经解决了。
我做的唯一一件事就是将属性从ViewConfigurationViewModel拉到DrawingsViewModel类,并绑定到那个类:

public ViewConfigurationViewModel? SelectedBaseViewConfiguration
{
    get => SelectedViewConfiguration?.BaseView;
    set => SelectedViewConfiguration.BaseView = value;
}

并将此属性添加到SelectedViewConfiguration。

[NotifyPropertyChangedFor(nameof(SelectedBaseViewConfiguration))]

那就没问题了我猜是因为它是嵌套的,视图只绑定到主视图,属性更改事件不起作用。

相关问题