MVVM Pattern 바인딩 문제

안녕하세요. C# WPF 프로그래밍 중에 ItemsSource 지정 관련 안되는 부분이 있어 글을 쓰게 되었습니다.

다름이 아니라, MVVM 패턴으로 source를 구현하고 있는데
MainViewModel에서
List<[Class Something]> Value = new List<[Class Something]>; 형태로 데이터를 지정하고,

MainWindow.xaml에서

<Window.DataContext>
    <ViewModel:MainViewModel/>
</Window.DataContext>
...
<ListView x:Name="myListView" SourceUpdated="myListView_SourceUpdated"
          ItemsSource="{Binding Value, UpdateSourceTrigger=PropertyChanged}">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="LAYER NUMBER" Width="100" 
                DisplayMemberBinding="{Binding MemberNode[0].Value, 
                    UpdateSourceTrigger=PropertyChanged}"/>
            <GridViewColumn Header="LAYER NAME"  Width="200" 
                DisplayMemberBinding="{Binding MemberNode[3].Value}"/>
        </GridView>
    </ListView.View>
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <EventSetter Event="PreviewMouseLeftButtonDown" 
                         Handler="ListViewItem_PreviewMouseLeftButtonDown" />
        </Style>
    </ListView.ItemContainerStyle>
</ListView>

으로 Binding 시에
Value 변수에 List<[Class Something]> 으로 할당된 데이터를 assign하는 형태인데

ListView 내용이 업데이트가 되지 않습니다 ㅠㅠ
코드 디버깅 실행 이후 Xaml 코드 상의 Binding된 데이터 "Value"를 지우고 다시 쓰면 데이터가 보이네요
혹시 이부분 같은 문제가 발생해서 해결 하셨던 분이나, 관련 내용 알고 계신분 있으시면 공유 부탁드리겠습니다!!

1개의 좋아요

재현 가능한 코드를 공유 주시면 도움 드릴 수 있을 것 같습니다.

2개의 좋아요

설명이 부족했습니다 ㅠㅠ
Node.cs

namespace Parser.Model
{
    public class Node : INotifyPropertyChanged
    {
        private string typeName;
        private string _value;
        private List<Node> memberNode;

        public string TypeName
        {
            set
            {
                typeName = value;
                PropertyChanged(this, new PropertyChangedEventArgs("TypeName"));
            }
            get
            {
                return typeName;
            }
        }

        public string Value
        {
            set
            {
                _value = value;
                PropertyChanged(this, new PropertyChangedEventArgs("Value"));
            }
            get
            {
                return _value;
            }
        }

        public List<Node> MemberNode
        {
            set
            {
                memberNode = value;
                PropertyChanged(this, new PropertyChangedEventArgs("MemberNode"));
            }
            get
            {
                return memberNode;
            }
        }
        public Node()
        {
            MemberNode = new List<Node>();
            TypeName = Constants.TYPE;
        }
        public Node(string strValue)
        {
            TypeName = "head";
            Value = strValue;
        }

        public Node(string strType, string strValue)
        {
            MemberNode = new List<Node>();
            TypeName = strType;
            Value = strValue;
        }
        public virtual string ToString()
        {
            return string.Empty;
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };

    }
}

MainModelView.cs

namespace Parser.ViewModel
{
    class MainViewModel : INotifyPropertyChanged
    {
        private string fileName; // file path name 
        private string fileText; // file contents

        public string FileText
        {
            set
            {
                fileText = value;
                PropertyChanged(this, new PropertyChangedEventArgs("FileText"));
            }
            get
            {
                return fileText;
            }
        }
        public string FileName
        {
            set
            {
                fileName = value;
                PropertyChanged(this, new PropertyChangedEventArgs("FileName"));

            }
            get
            {
                return fileName;
            }
        }
        public BtnCommand btn_cmd { get; set; }

        //Parsing Data
        public Dictionary<string, Model.Node> myNode;


        //LAY Information Object ( this variable directing Node inforamtion )
        private List<Model.Node> myLAYObject = new List<Model.Node>();
        public List<Model.Node> MyLAYObject
        {
            set
            {
                myLAYObject = value;
                PropertyChanged(this, new PropertyChangedEventArgs("MyLAYObject"));
            }
            get
            {
                return myLAYObject;
            }
        }

        public MainViewModel()
        {
            btn_cmd = new BtnCommand(ExecuteFunc, CanExecuteFunc);
            myNode = new Dictionary<string, Model.Node>(); 
        }

        private void ExecuteFunc(object obj)
        {
            //Execute Command Action
            var objValue = obj as string;
            if( objValue == Constants.BTNCOMMANDNAME.MENUOPEN)
            {
                OpenFileDialog openFileDialog = new OpenFileDialog();
                if (openFileDialog.ShowDialog() == true)
                    fileName = openFileDialog.FileName;
                fileText = File.ReadAllText(fileName);
                fileText = fileText.Replace("\n", "");
                fileText = fileText.Replace("\r", "");
                //Parsing Data 
                if (fileText.Length > 0)
                {
                    int pointer = 0;
                    while (fileText.Length > pointer)
                    {
                        try
                        {
                            var tempNode = Parser.Util.CParser.Parser(fileText, ref pointer, "root");
                            if (!myNode.ContainsKey(tempNode.Value))
                            {
                            }
                            myNode[tempNode.Value] = tempNode;
                        }
                        catch (Exception Ex)
                        {
                            //Something else
                        }
                        pointer++;
                    }
                }
                //LAY Information Add
                myLAYObject = myNode["VALUE"].MemberNode; //List<LAY>
                //myListView.ItemsSource = myLAYObject;
            }
        }
        private bool CanExecuteFunc(object obj)
        {
            return true;
        }

        public void BindingResource(object obj)
        {
            //LAY Information Add
            myLAYObject = myNode["LIB_LAYER"].MemberNode; //List<LAY>
            
        }

        public event PropertyChangedEventHandler PropertyChanged = delegate { };
    }
}

MainView.xaml

<Window x:Class="Parser.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ViewModel="clr-namespace:Parser.ViewModel"
        xmlns:local="clr-namespace:Parser"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <ViewModel:MainViewModel x:Name="MainViewModelV"/>
    </Window.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="5*"/>
            <RowDefinition Height="90*" />
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20*" />
                <ColumnDefinition Width="50*" />
            </Grid.ColumnDefinitions>
            <Menu DockPanel.Dock="Top" Grid.Column="0">
                <MenuItem Header="_File">
                    <MenuItem Header="Open" x:Name="mnOpen" Command="{Binding btn_cmd}" CommandParameter="mnOpen" />
                    <Separator />
                    <MenuItem Header="_Exit" />
                </MenuItem>
                <MenuItem Header="_Edit">
                    <MenuItem Command="Cut" />
                    <MenuItem Command="Copy" />
                    <MenuItem Command="Paste" />
                </MenuItem>
            </Menu>
            <Label Grid.Column="1" Content="{Binding Path=FileName}" />
        </Grid>
        
        <TabControl Grid.Row="1">
            <TabItem Header="MainPage">
                <Grid>
                    <!-- MainPage-->
                    <Grid.RowDefinitions>
                        <RowDefinition Height="10*" />
                        <RowDefinition Height="50*" />
                        <RowDefinition Height="50*" />
                    </Grid.RowDefinitions>
                    <Grid Grid.Row="0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="10*" />
                            <ColumnDefinition Width="10*" />
                            <ColumnDefinition Width="10*" />
                            <ColumnDefinition Width="10*" />
                            <ColumnDefinition Width="10*" />
                        </Grid.ColumnDefinitions>
                        <Button x:Name="btnTest" Content="Test Open" Click="Button_Click" />
                    </Grid>
                </Grid>
            </TabItem>
            <TabItem Header="LayerView">
                <Grid>
                    <ListView x:Name="myListView" ItemsSource="{Binding MyLAYObject, UpdateSourceTrigger=PropertyChanged}" SourceUpdated="myListView_SourceUpdated">
                        <ListView.View>
                            <GridView>
                                <GridViewColumn Header="NUMBER" Width="100" DisplayMemberBinding="{Binding MemberNode[0].Value, UpdateSourceTrigger=PropertyChanged}" />
                                <GridViewColumn Header="LAYER NAME" Width="200" DisplayMemberBinding="{Binding MemberNode[3].Value}"/>
                            </GridView>
                        </ListView.View>
                        <ListView.ItemContainerStyle>
                            <Style TargetType="ListViewItem">
                                <EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListViewItem_PreviewMouseLeftButtonDown" />
                            </Style>
                        </ListView.ItemContainerStyle>
                    </ListView>
                </Grid>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

Node에 들어 있는 Data List의 경우
MainWindow.xaml.cs 에서 myListView에 itemSource를 코드상으로 지정하면 반영이 됩니다. 뿐만아니라 디버깅 모드에서 MainWindow.xaml에서 MyLAYObject 값을 지우고 다시 입력하면 ListView에 데이터가 반영이 되는데,
제가 궁금한 부분은 xaml에서 binding을 이미 했는데 MyLAYObject의 값을 다음의 형식대로
myLAYObject = myNode[“VALUE”].MemberNode; //List 을 통해 값 업데이트를 못하는건가요?

Deep Copy를 통해 데이터만 다시 입력 해야되는지 알고 계신부분 있으시면 공유 부탁드리겠습니다! ㅠㅠ

1개의 좋아요

맴버 필드에 값을 대입하는게 아니고 속성에 해야 하지 않나요?
속성 set에서 노티파이 하니깐요

MyLAYObject =  myNode[“VALUE”].MemberNode;

참고로 모든 컬렉션을 다시 갱신하는 방식 말고 observablecollection 을 이용해서 Add / Remove 하면
자동으로 노티파이 되서 뷰에 반영 됩니다.

5개의 좋아요

제가 실수가 있었네요 감사합니다 ㅠㅠ

1개의 좋아요