使用MVVM在WPF中与ItemSource绑定/数据上下文问题

使用MVVM在WPF中与ItemSource绑定/数据上下文问题,wpf,xaml,mvvm,binding,datacontext,Wpf,Xaml,Mvvm,Binding,Datacontext,我有一个ViewModel,它是一个窗口,在这个窗口中有许多我制作的用户控件。这些工作很好,并且适当地设置了每个的绑定和数据上下文;除了一个 在我的MainWindowView XAML中,我有 <Controls:LogViewerView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DataContext="{Bindi

我有一个ViewModel,它是一个窗口,在这个窗口中有许多我制作的用户控件。这些工作很好,并且适当地设置了每个的绑定和数据上下文;除了一个

在我的MainWindowView XAML中,我有

<Controls:LogViewerView HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        DataContext="{Binding LogViewerViewModel}"/>
LogViewerView

我们在Styles.xaml中的位置

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:Caliburn="http://www.caliburnproject.org" 
                    xmlns:Models="clr-namespace:GambitFramework.Utilities.Models">
    <Style x:Key="LogViewerStyle" TargetType="ItemsControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate>
                    <ScrollViewer CanContentScroll="True">
                        <ItemsPresenter/>
                    </ScrollViewer>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Setter Property="ItemsPanel">
            <Setter.Value>
                <ItemsPanelTemplate>
                    <VirtualizingStackPanel IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </Setter.Value>
        </Setter>
    </Style>
    <DataTemplate DataType="{x:Type Models:LogEntry}">
        <Grid IsSharedSizeScope="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition SharedSizeGroup="Timestamp" Width="Auto"/>
                <ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
                <ColumnDefinition SharedSizeGroup="IconSource" Width="Auto"/>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="{Binding Timestamp}" 
                       Grid.Column="0"
                       FontWeight="Bold" 
                       Margin="5,0,5,0"/>
            <TextBlock Text="{Binding Index}" 
                       Grid.Column="1"
                       FontWeight="Bold" 
                       Margin="0,0,2,0" />
            <TextBlock Text="{Binding Message}" 
                       Grid.Column="3"
                       TextWrapping="Wrap"/>
        </Grid>
    </DataTemplate>
</ResourceDictionary>
但是当我使用Snoop检查绑定时,我的项目不会显示

无法设置表达式。它被标记为“不可共享”,并且已被使用

这显然表明DataContext设置不正确。我在这里做错了什么?为什么没有为我的控件设置DataContext

非常感谢您抽出时间

编辑。下面是一个答案,使用相同的日志控件,但绑定到后面的代码,我想绑定到一个单独的文件:

基于以下语句:编辑。下面是使用相同日志的答案 控件但是绑定到后面的代码,我想绑定到一个单独的 文件:

请参阅以下代码,了解代码隐藏并创建viewmodel

 public partial class MainWindow : Window
{       
    public MainWindow()
    {
        InitializeComponent();
        MainViewModel vm = new MainViewModel();
        this.DataContext = vm.LogEntries;           
    }        
}


class MainViewModel
{
    private string TestData = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum";
    private List<string> words;
    private int maxword;
    private int index;

    public ObservableCollection<LogEntry> LogEntries { get; set; }

    public MainViewModel()
    {
        random = new Random();
        words = TestData.Split(' ').ToList();
        maxword = words.Count - 1;

         LogEntries = new ObservableCollection<LogEntry>();
        Enumerable.Range(0, 200000)
                  .ToList()
                  .ForEach(x => LogEntries.Add(GetRandomEntry()));

        Timer = new Timer(x => AddRandomEntry(), null, 1000, 10);
    }

    private System.Threading.Timer Timer;
    private System.Random random;
    private void AddRandomEntry()
    {
        System.Windows.Application.Current.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, (Action)(() =>
        {
           LogEntries.Add(GetRandomEntry());
        }));
    }

    private LogEntry GetRandomEntry()
    {
        if (random.Next(1, 10) > 1)
        {
            return new LogEntry()
            {
                Index = index++,
                DateTime = DateTime.Now,
                Message = string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
                                                     .Select(x => words[random.Next(0, maxword)])),
            };
        }

        return new CollapsibleLogEntry()
        {
            Index = index++,
            DateTime = DateTime.Now,
            Message = string.Join(" ", Enumerable.Range(5, random.Next(10, 50))
                                         .Select(x => words[random.Next(0, maxword)])),
            Contents = Enumerable.Range(5, random.Next(5, 10))
                                 .Select(i => GetRandomEntry())
                                 .ToList()
        };

    }
}

public class LogEntry : PropertyChangedBase
{
    public DateTime DateTime { get; set; }

    public int Index { get; set; }

    public string Message { get; set; }
}

public class CollapsibleLogEntry : LogEntry
{
    public List<LogEntry> Contents { get; set; }
}

public class PropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        Application.Current.Dispatcher.BeginInvoke((Action)(() =>
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }));
    }
}
在MainWindowView中,您可能有:

public LogViewerViewModel LogViewerViewModel { get; set; }
但仅此而已。您没有声明您正在将其初始化为任何内容。话虽如此,我认为你需要有这样的东西:

public MainWindowView()
{
    LogViewerViewModel = new LogViewerViewModel();

    //Doing everything else here
}

其他一切看起来都很好,所以我唯一的想法是您没有初始化视图模型。检查输出窗口是否存在任何其他绑定错误。确保你的收藏中也有物品。

在我看来,有几件事可以帮助你,首先

public LogViewerViewModel LogViewerViewModel { get; set; }
我看不出您在何处引用了它,但如果定义得太晚,视图将无法读取它,因此您可以在viewmodels中实现PropertyChangedBase,也就是InotifyedPropertyChanged

我几乎可以肯定,您使用的是null而不是值

如果没有,请检查以下各项:

另一件事是确保customs控件的所有属性都是依赖性属性,这些属性由控件的名称很好地定义

您可以测试以下内容,而不是将其放置在itemscontrol的ItemTemplate内的style place中,因为我直接看到的是DataTemplate,而不是

<Setter Property="ItemTemplate"><Setter.Value>

您是否尝试过显式地使用绑定:

<Controls:LogViewerView HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        DataContext="{Binding MainWindowViewModel.LogViewerViewModel, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Views:MainWindowView}}}"/>

如果看不到MainWindowView的更多内容,我想你已经有了一个DataContext,介于劫持你的意图之间。

我已经研究了你的代码,没有看到为MainWindowView设置DataContext。这样做有很多选择。例如,有两种方法:

首先-在MainWindowView.xaml.cs中设置创建和设置视图模型:

public MainWindow()
{
    InitializeComponent();
    DataContext = new MainWindowViewModel();
}
第二-在MainWindowView.xaml中创建并设置视频模型:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:test="clr-namespace:Test"
    Title="MainWindow"
    Width="525"
    Height="350">
<Window.DataContext>
    <test:MainWindowViewModel/>
</Window.DataContext>
当你做了上面的一个,它应该只是工作

我还注意到,您注意到LogViewerView中的redundunt代码,它可以是:

<DockPanel>
    <ItemsControl ItemsSource="{Binding LogEntries}" Style="{StaticResource LogViewerStyle}" />
</DockPanel>
因为您已经在ResourceDictionary中使用LogViewerStyle编写了该代码


希望这有帮助。

您将LogViewerViewModel绑定到MainWindowView的DataContext,而不是LogViewerView的DataContext

如果要从父级的DataContext派生,请查看类似的问题,如:


请注意,DataTemplate有点特殊:

只需将LogViewerViewModel设置为DataContext,这正是我在上面的代码中显示的内容。。。很抱歉,我把代码弄得乱七八糟,想看看到底出了什么问题。。。编辑!:]如何创建LogViewerViewModel的实例?通过IEnumerable LogViewerViewModel=new LogViewerViewModel new ListlogEntries.OrderBye=>e.Timestamp;我可以说这可能是可行的,但在我上面的真实示例中,绑定/数据上下文设置不正确,我基本上是在做您正在做的事情。。。我真的不确定我的代码是怎么回事,但我可以说上面的方法是有效的。但是,如果您有任何关于生产代码不能正确绑定的想法,我们将不胜感激。。。谢谢你的时间。你能分享一些模拟问题的工作示例吗?这将有助于解决您的问题。您好,我实际上正在正确初始化ViewModel LogViewServiceWModel,即在父ViewModel的ctor中,我有logViewer=new LogViewServiceWModel;或者别的什么。我还可以看到通过断点正确填充logViewer.LogEntries。可能与数据模板的使用有关吗?这是怎么回事。。。非常感谢您的时间和复活节快乐!您好,实际上我正在正确地初始化ViewModel LogViewServiceWModel,即在父ViewModel的ctor中,并且相当早,以便同时实例化其他控件。在ctor中,我有logViewer=newlogviewerviewmodel;。我还可以看到logViewer.LogEntries通过断点正确填充。可能与数据模板的使用有关吗?这是怎么回事。。。非常感谢您的时间和复活节快乐!那么,在调试器下运行时,您会看到哪些绑定错误 惯性导航与制导?
<Controls:LogViewerView HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"
                        DataContext="{Binding MainWindowViewModel.LogViewerViewModel, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Views:MainWindowView}}}"/>
xmlns:Views="clr-namespace:GambitFramework.Utilities.Controls.Views"
public MainWindow()
{
    InitializeComponent();
    DataContext = new MainWindowViewModel();
}
<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:test="clr-namespace:Test"
    Title="MainWindow"
    Width="525"
    Height="350">
<Window.DataContext>
    <test:MainWindowViewModel/>
</Window.DataContext>
<DockPanel>
    <ItemsControl ItemsSource="{Binding LogEntries}" Style="{StaticResource LogViewerStyle}" />
</DockPanel>