Причина, по которой вы получаете эту ошибку, заключается в том, что вы устанавливаете содержимое ScrollViewer как UserControl.Тем самым вы устанавливаете родительский элемент UserCtrl1 (UserControl) в ScrollViewer.Если вы не можете установить двух дочерних элементов в ScrollViewer, что и происходит, когда вы пытаетесь установить UserCtrl2 в качестве ActiveFunction.Что вы действительно должны делать, так это слишком использовать возможности ViewModels и DataTemplates в WPF.
Из кода, который вы опубликовали, я изменил его, чтобы использовать более MVVM-подход.
- Использование ViewModels и DataTemplates.Лучше использовать viewmodels, потому что это чистый код, вам больше не нужно возиться с этими отношениями родительского / дочернего интерфейса.Вы просто устанавливаете вещи как обычные объекты.Указав табличку данных для определенного класса, вы сделали визуальный дисплей для вас.Это полное разделение между кодом и визуальными аспектами.
- Команды.Я использую команду для обработки нажатия кнопки.Это то, как вы должны это делать, если хотите пойти по маршруту MVVM.Помимо помощи в разделении логики и представления, вы также можете выполнять модульные тесты для команд, также с необходимостью пользовательского интерфейса.
Вот MainWindow.
<Window x:Class="LogicalChildException.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:LogicalChildException"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type local:UserControl1ViewModel}">
<local:UserControl1 />
</DataTemplate>
<DataTemplate DataType="{x:Type local:UserControl2ViewModel}">
<local:UserControl2 />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<DockPanel>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal">
<Button Command="{Binding SwitchCommand}">Change UserControl</Button>
</StackPanel>
<ScrollViewer Content="{Binding ActiveFunction}">
</ScrollViewer>
</DockPanel>
Вот код для MainWindow.xaml.cs.В основном то, что я делаю здесь, я устанавливаю DataContext этого представления в качестве модели представления.Это не лучший способ сделать это, потому что вы жестко программируете вещи.Лучше было бы использовать шаблоны данных и позволить WPF обрабатывать их.
using System.Windows;
namespace LogicalChildException
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
}
Вот код для моделей представления.Я использовал идею DelegateCommand, которую нашел здесь http://www.wpftutorial.net/DelegateCommand.html. UserControl1ViewModel и UserControl2ViewModel - просто фиктивные объекты, но вы можете заставить их реализовать INotifyPropertyChanged, а затем использовать его для привязки в вашей таблице данных.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Windows.Input;
namespace LogicalChildException
{
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ICommand _switchCommand;
private object _activeFunction;
private object _userControl1;
private object _userControl2;
public MainViewModel()
{
_switchCommand = new DelegateCommand(OnSwitch);
_userControl1 = new UserControl1ViewModel();
_userControl2 = new UserControl2ViewModel();
ActiveFunction = _userControl1;
}
public ICommand SwitchCommand
{
get
{
return _switchCommand;
}
}
public object ActiveFunction
{
get
{
return _activeFunction;
}
set
{
if (_activeFunction != value)
{
_activeFunction = value;
OnPropertyChanged("ActiveFunction");
}
}
}
private void OnSwitch(object obj)
{
// do logic for switching "usercontrols" here
if (ActiveFunction == null)
{
// if null, just set it to control 1
ActiveFunction = _userControl1;
}
else
{
ActiveFunction = (ActiveFunction is UserControl1ViewModel) ? _userControl2 : _userControl1;
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class UserControl1ViewModel
{
}
public class UserControl2ViewModel
{
}
}
Здесь есть много областей, которые вы могли бы улучшить, чтобы сделать чище в мире MVVM, но это должно помочь вам решить вашу проблему, которая у вас есть в настоящее время.