简体   繁体   English

在视图内查看不更新 - Caliburn.Micro

[英]View within a view not updating - Caliburn.Micro

I'm having an issue where a datagrid is not reflecting changes to its collection when attached to a view inside a view. 我遇到的问题是,当数据网格附加到视图中的视图时,它不会反映其集合的更改。 More accurately, I have a SecondView within the MainView. 更准确地说,我在MainView中有一个SecondView。 On the SecondView I have a datagrid with autogeneratecolumns set to true; 在SecondView上我有一个datagrid,autogeneratecolumns设置为true; when the datagrid is first rendered, it displays the appropriate columns and headers. 首次呈现数据网格时,它会显示相应的列和标题。 However, when I populate the list that is attached to it, no changes are reflected. 但是,当我填充附加到它的列表时,不会反映任何更改。

Here is the complete code for the two views and their respective viewmodels: MainWindowView: 以下是两个视图及其各自视图模型的完整代码:MainWindowView:

<Window x:Class="MyApp.MainWindowView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:cal="http://www.caliburnproject.org"
    xmlns:views="clr-namespace:MyApp"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindowView" Height="300" Width="300">
<Grid>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Open" x:Name="Open"/>
                <MenuItem Header="Exit" x:Name="Exit"/>
            </MenuItem>
        </Menu>
        <StackPanel DockPanel.Dock="Bottom">
            <views:SecondView/>
        </StackPanel>
    </DockPanel>
</Grid>

MainWindowViewModel: MainWindowViewModel:

namespace MyApp
{
[Export(typeof(IShell))]
internal class MainWindowViewModel : Screen, IShell
{
    Regex expression = new Regex(@"^N\d\.C\d\.D\d\.R\d:\s\s\s-\d"); //ex. "N1.C1.D2.R1:   -3"        
    SecondViewModel svm = new SecondViewModel();        
    public void Open()
    {
        Microsoft.Win32.OpenFileDialog openFile = new Microsoft.Win32.OpenFileDialog();
        openFile.Multiselect = true;
        openFile.Filter = "Text Files(*.txt)|*.txt|Log Files(*.log)|*.log|All Files(*.*)|*.*";
        openFile.Title = "Open File(s)";
        bool? userClickedOK = openFile.ShowDialog();
        string[] _fileNames = openFile.FileNames;
        if (userClickedOK == true)
        {
            if (_fileNames != null)
            {
                for (int i = 0; i < _fileNames.Length; i++)
                {
                    ValidFiles(_fileNames[i]);
                }
            }
        }
    }
    public void Exit()
    {
        App.Current.Shutdown();
    }
    /* ValidFiles() accepts a string containing a filename and creates a Streamreader that reads the file if it is not a Boxboro file.
     */
    public void ValidFiles(string filename)
    {
        string line;
        using (StreamReader sr = new StreamReader(filename))
        {
            while ((line = sr.ReadLine()) != null)
            {
                if (line.Contains("Mono Status"))
                {
                    Console.WriteLine("File(s) not supported by this parser. Please select a valid file.");
                    break;
                }
                else
                {
                    IsMatch(line);
                }
            }
        }
    }
    /* IsMatch() accepts a string "input" and determines which parsing method to send the string to, if any.
     * Strings not matching any of the initial criteria are not processed to limit overhead.
     */
    public void IsMatch(string input)
    {
        Match match = expression.Match(input);
        if (match.Success)
        {
            svm.GetData(input);
        }
    }
}

} }

SecondWindowView: SecondWindowView:

<UserControl x:Class="MyApp.SecondView"
         xmlns:cal="http://www.caliburnproject.org"
         cal:Bind.Model="MyApp.SecondViewModel"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300">
<Grid>
    <StackPanel>
        <DataGrid x:Name="MyList"/>
    </StackPanel>
</Grid>

SecondWindowViewModel: SecondWindowViewModel:

namespace MyApp
{
[Export(typeof(SecondViewModel))]
class SecondViewModel:Screen
{
    Parse parse = new Parse();
    BindableCollection<MyObject> myList = new BindableCollection<MyObject>();
    MyObject myObject;
    public MyObject MyObject
    {
        get { return myObject; }
        set
        {
            myObject = value;
            NotifyOfPropertyChange(() => MyList);
        }
    }
    public BindableCollection<MyObject> MyList
    {
        get { return myList; }
        set 
        { 
            MyList = value;
            NotifyOfPropertyChange(() => MyList);
        }
    }
    public void GetData(string input)
    {
        string[] tempArray = input.Split();
        List<int> tempList = new List<int>();
        for (int i = 1; i < tempArray.Length; i++)
        {
            if (!string.IsNullOrEmpty(tempArray[i]))
            {
                tempList.Add(Convert.ToInt32(tempArray[i]));
            }
        }
        int[] tempIntArray = tempList.ToArray();
        MyObject = new MyObject(tempArray[0], tempIntArray[0], tempIntArray[1], tempIntArray[2], tempIntArray[3]);
        this.MyList.Add(MyObject);

        Console.WriteLine("MyList has " + MyList.Count.ToString() + " elements.");
        //foreach (MyObject item in MyList)
        //{
        //    Console.WriteLine(item.Location);
        //}
    }
}

} }

Boostrapper: Boostrapper:

namespace MyApp
{
    internal class AppBootStrapper : Bootstrapper<IShell>
    {
        static AppBootStrapper()
        {
            //Initializes the logger for debugging, remove or comment out in release.
            LogManager.GetLog = type => new DebugLogger(type);
        }
        private CompositionContainer container;
        protected override void BuildUp(object instance)
        {
            this.container.SatisfyImportsOnce(instance);
        }
        protected override void Configure()
        {
            this.container = 
                new CompositionContainer(
                    new AggregateCatalog(
                        AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
            var batch = new CompositionBatch();
            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(this.container);
            this.container.Compose(batch);
        }
        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return this.container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }
        //This method is required for the BootStrapper.cs to be discovered.
        protected override object GetInstance(Type serviceType, string key)
        {
            string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
            IEnumerable<object> exports = this.container.GetExportedValues<object>(contract);
            if (exports.Count() > 0)
            {
                return exports.First();
            }
            throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
        }
    }
}

Based on my understanding of Caliburn.Micro, whenever the observablecollection MyList is updated (a new item added), the datagrid with x:name MyList should be updated. 基于我对Caliburn.Micro的理解,每当更新observablecollection MyList(添加新项目)时,应更新具有x:name MyList的数据网格。 Even without a data template, I would think I would see a list of blank entries equivalent in length to the number of objects in MyList. 即使没有数据模板,我想我会看到一个空白条目列表,其长度与MyList中的对象数量相当。 When I use this same code in the MainViewModel, rather than in a usercontrol bound to the MainView, I have no issues rendering the list. 当我在MainViewModel中使用相同的代码时,而不是在绑定到MainView的用户控件中,我没有问题呈现列表。 It seems I'm missing something about updating a view within a view. 我似乎缺少一些关于在视图中更新视图的内容。

I should note, I can verify the list has objects in it by using Console.WriteLine(MyList.Count.ToString()) and watching the output window. 我应该注意,我可以通过使用Console.WriteLine(MyList.Count.ToString())并观察输出窗口来验证列表中是否有对象。 I hate asking about these things, every time I do it ends up being a typo or something equally silly, but I've been stuck here for too long. 我讨厌询问这些事情,每次我这样做都会成为一个错字或同样愚蠢的东西,但我已经被困在这里太久了。

NOTE: Even with MyList.Refresh() thrown in on each iteration, no changes in the datagrid occur. 注意:即使在每次迭代时抛出MyList.Refresh(),也不会发生数据网格的更改。

NOTE: It seems like this may answer my question, but I don't understand how to implement it. 注意:似乎这可能会回答我的问题,但我不明白如何实现它。 Perhaps if someone else understands it better, they could put the lines of code in the appropriate places in my code and explain why it works. 也许如果其他人更好地理解它,他们可以将代码行放在我的代码中的适当位置并解释其工作原理。 Thanks in advance. 提前致谢。 Caliburn.Micro convention-based bindings not working in nested views? Caliburn.Micro基于约定的绑定在嵌套视图中不起作用?

Try this viewmodel first approach - I suspect your inner view isn't being bound (CM doesn't look across control boundaries when applying conventions eg it won't apply conventions to nested usercontrols) 尝试这种viewmodel第一种方法 - 我怀疑你的内部视图没有被绑定(CM在应用约定时不会跨越控制边界,例如它不会对嵌套用户控件应用约定)

<Window x:Class="MyApp.MainWindowView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:cal="http://www.caliburnproject.org"
    xmlns:views="clr-namespace:MyApp"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindowView" Height="300" Width="300">
<Grid>
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="File">
                <MenuItem Header="Open" x:Name="Open"/>
                <MenuItem Header="Exit" x:Name="Exit"/>
            </MenuItem>
        </Menu>
        <StackPanel DockPanel.Dock="Bottom">
            <!-- Use ContentControl for sub-views, CM will do it's magic if you bind to the VM property using the standard conventions -->
            <ContentControl x:Name="SecondView" />
        </StackPanel>
    </DockPanel>
</Grid>

Then in your main: 然后在你的主要:

internal class MainWindowViewModel : Screen, IShell
{
    Regex expression = new Regex(@"^N\d\.C\d\.D\d\.R\d:\s\s\s-\d"); //ex. "N1.C1.D2.R1:   -3"      
    // Declare your second VM as a property so you can bind to it via CM conventions  
    public SecondViewModel SecondView 
    { 
        get { return _secondView; } 
        set 
        {
            _secondView = value;
            NotifyOfPropertyChange(() => SecondView);
        }
    }

    public MainWindowViewModel()
    {
        SecondView = new SecondViewModel();
    }

CM will automatically inject the right view into the content controls template and setup the datacontext CM将自动将正确的视图注入内容控件模板并设置datacontext

Alternatively, you can use Bind.Model to bind the VM instance to the view which is more a view-first approach 或者,您可以使用Bind.Model将VM实例绑定到视图,这更像是一种视图优先方法

   <StackPanel DockPanel.Dock="Bottom">
        <views:SecondView cal:Bind.Model="{Binding SecondView}" />
    </StackPanel>

(I think it's Bind.Model and not View.Model but I often get the two mixed up, so failing Bind.Model try View.Model) (我认为它是Bind.Model而不是View.Model,但我经常把两者搞混,所以Bind.Model失败了,试试View.Model)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM