【问题标题】:Exception Occurring Infinitely in Content Control内容控制中无限发生的异常
【发布时间】:2014-04-28 15:38:50
【问题描述】:

首先,这是一个使用 MVVM 的向导控件的简化版本。如下所述,该问题更容易重现

经过大量缩小后,我已经解决了由于 WPF ContentControl 导致的代码中的无限异常。但是,除了尝试捕获所有可能的实例化代码之外,我还没有弄清楚如何处理它。这是重现此情况的示例代码...任何有关如何防止此无限异常发生的帮助将不胜感激。

其他详情

总结一下,问题是如果内容控件改变了它的内容,被加载的东西抛出异常,那么它就会抛出异常,然后重试加载,导致一次又一次的抛出。

MainWindow.xaml

<Window x:Class="WpfApplication8.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Name ="Main">
    <Grid>

        <ContentControl Name="bar" Content="{Binding ElementName=Main, Path=foo}"/>
    <Button Click="ButtonBase_OnClick" Margin="20" Width="50"/>
  </Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private UserControl _foo;
    public UserControl foo
    {
        get { return _foo; }
        set { _foo = value; OnPropertyChanged("foo"); }
    }

    public MainWindow()
    {
        InitializeComponent();
        foo = new UserControl1();
    }

    private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
    {
        foo = new UserControl2();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

UserControl1 为空白且全部默认

UserControl2.xaml.cs

public UserControl2()
{

  InitializeComponent();
  throw new Exception();
}

【问题讨论】:

  • 您能否详细说明 ContentControl 如何导致无限循环?
  • 删除所有这些并创建一个适当的 ViewModel 并从 UI 中删除不属于它的 INotifyPropertyChanged 并使用适当的 DataBinding 和 DataTemplates,您的所有问题都会神奇地消失。
  • “无限异常”是什么意思?无限循环? StackOverflowException?
  • @YuvalItzchakov 我添加了其他细节,希望有助于澄清。即使出现异常,也会不断重试渲染的是内容更改渲染。
  • @HighCore 这是简化版本,但这发生在使用 DataBindings 和 ViewModels 的生产代码中。它是一个 WizardFramework,随着步骤的进行,用户控件会被替换掉。

标签: c# .net wpf xaml


【解决方案1】:

不要将 ContentControl 绑定到 MainWindow。而是使用DataTemplates 来选择主窗口的内容。一种设计方法是将 ContentControl 的内容绑定到 MainWindow 的 DataContext。

首先需要一些可观察的测试数据。这些数据的细节并不重要。要点是有两种不同类别的测试数据可供选择 - TestData.cs

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace fwWpfDataTemplate
{
    // Classes to fill TestData
    public abstract class Person : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
    }
    public class Student : Person { }
    public class Employee : Person
    {
        float _salary;
        public float Salary
        {
            get { return _salary; }
            set
            {
                _salary = value;
                OnPropertyChanged("Salary");
            }
        }
    }

    public class TestData : ObservableCollection<Person>
    {
        public TestData()
            : base(new List<Person>()
            { 
                new Student { Name = "Arnold" },
                new Employee { Name = "Don", Salary = 100000.0f }
            }) { }
    }
}

然后将 DataTemplates 添加到 MainWindow 的资源 - MainWindow.xaml

<Window x:Class="fwWpfDataTemplate.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:me="clr-namespace:fwWpfDataTemplate"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <DataTemplate DataType="{x:Type me:Student}">
            <StackPanel>
                <TextBlock Text="Student"/>
                <TextBlock Text="{Binding Name}"/>
            </StackPanel>
        </DataTemplate>
        <DataTemplate DataType="{x:Type me:Employee}">
            <StackPanel>
                <TextBlock Text="Employee"/>
                <TextBlock Text="{Binding Name}"/>
                <TextBlock Text="Salary"/>
                <TextBlock Text="{Binding Salary}"/>
            </StackPanel>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Button Content="Change Data Context" Click="Button_Click" />
        <ContentControl Grid.Row="1" Content="{Binding}"/>
    </Grid>
</Window>

注意:DataTemplates 的内容可以是 UserControl1、UserControl2 等,而不是 StackPanels。

然后添加一些代码来更改数据上下文 - MainWindow.cs

using System.Windows;

namespace fwWpfDataTemplate
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        TestData testData = new TestData();
        int testIndex = -1;
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            testIndex = (testIndex + 1) % testData.Count;
            this.DataContext = testData[testIndex];
        }
    }
}

享受吧。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-05
    • 2019-03-09
    • 2016-08-08
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 2020-07-03
    相关资源
    最近更新 更多