ObservableCollection으로 대분류 그리고 그 안의 ObservableCollection을 받아서 UI상에서 업데이트 시키는 방법

안녕하세요! C#의 기본적인 기능이자 WPF에서 주로 사용하는 ObservableCollection 사용에 대해 질문이 있어서 글을 올립니다.

현재 WinUI3 를 이용해 프로젝트를 진행하고 있는데요, DB를 사용해서 값을 저장하고 그 저장된 값을 UI에서 계속적으로 데이터를 갱신 또는 누적시키는 부분에서 진도가 막혀 있는 상황입니다.

코드를 통해서 설명하자면

[Model]

// WinUI3 프로젝트는 Devexpress의 MVVM 모델을 사용합니다
//  // DB 테이블 모델이 아닌 테이블의 데이터를 받아서 가공하기 위한 모델입니다.

public class ViewGroup : ViewModelBase
{
    public int id { get; set; }
    public string groupName { get; set; }

    public ObservableCollection<ViewData > ViewDatas { get => GetValue<ObservableCollection<ViewData >>(); set => SetValue(value); }
}

public class ViewData : ViewModelBase
{
    public int DataId { get; set; }
    public int Number { get; set; }
    public string CH_Name { get => GetValue<string>(); set => SetValue(value); }
    public double? CH_Value { get => GetValue<double?>(); set => SetValue(value); }
    public DateTime? WriteDate { get => GetValue<DateTime>(); set => SetValue(value); }
    public ViewGroup ViewGroups { get; set; }
}

ViewGroup이라는 Model안에 DB에 저장된 데이터를 넣고, ObservableCollection ViewDatas를 이용하여 데이터들을 ObservableCollection 형태로 받아서 지속적으로 UI를 업데이트 시키는 코드를 구성하려고 했습니다.
여기서 변하는 UI는 ViewData의 들어오는 데이터들만 변하고, ViewGroup은 처음 할당 받으면 그 이상 변하지 않도록 코드를 작성하고 싶어요.

[XAML]

<ListView ItemsSource="{Binding ViewGroupsOB}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Vertical">
                <TextBlock Text="{Binding groupName}" />

                <ListView ItemsSource="{Binding ViewData}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="{Binding CH_Name}" />
                                <StackPanel Orientation="Horizontal">
                                    <TextBlock Text="{Binding CH_Value, Mode=TwoWay}" />
                                    <TextBlock Text="{Binding WriteDate}" />
                                </StackPanel>
                            </StackPanel>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
            </StackPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

[ViewModel]

public async void ViewDataReNew()
{
    ViewGroupsOB = new ObservableCollection<ViewGroup>();
  using (var newdbContext = new DataAccessEFCore())
  {
      var groupSelectionAll = newdbContext.SettingGroupConfigs
                                        .Include(i => i.SettingSingleConfigs)
                                        .ThenInclude(i2 => i2.DeviceChannel)
                                        .ThenInclude(i3 => i3.DeviceDatas)
                                        .ToObservableCollection();

      HashSet<int> groupIds = new HashSet<int>(); // 새로운 Group 객체를 저장할 HasSet 생성

    while (true)
    {
        foreach (var item in groupSelectionAll)
        {
            if (!groupIds.Contains(item.Id))
            {
                var ViewGroup = new ViewGroup
                {
                    id = item.Id,
                    groupName = item.GroupName,
                    ViewData = new ObservableCollection<ViewData >()
                };

                groupIds.Add(item.Id);


                foreach (var item2 in item.SettingSingleConfigs)
                {
                    var ViewDATA = new ViewData 
                    {
                        DataId= item2.Id,
                        Number = item2.TypeNumber,
                        CH_Name = item2.DeviceChannel.DeviceChannelName,
                        CH_Value = item2.DeviceChannel.DeviceDatas.GroupBy(g => g.DeviceChannels.DeviceChannelId)
                                                                .Select(s => s.OrderByDescending(o => o.WriteDate).First())
                                                                .FirstOrDefault(w => w.DeviceChannels != null).CH_Value,
                        ViewGroups = ViewGroup
                    };

                    // 데이터 지속적으로 업데이트 하는 코드
                    ViewDATA.CH_Value = item2.DeviceChannel.DeviceDatas.GroupBy(g => g.DeviceChannels.DeviceChannelId)
                                                    .Select(s => s.OrderByDescending(o => o.WriteDate).First())
                                                    .FirstOrDefault(w => w.DeviceChannels != null).CH_Value;
                    ViewDATA.WriteDate = item2.DeviceChannel.DeviceDatas.GroupBy(g => g.DeviceChannels.DeviceChannelId)
                        .Select(s => s.OrderByDescending(o => o.WriteDate).First())
                        .FirstOrDefault(w => w.DeviceChannels != null).WriteDate;
                    //여기부터 수정

                    ViewGroup.ViewChannelData.Add(ViewDATA);
                }


                if (ViewGroupsOB.Any(a => a.id != item.Id))
                {
                    ViewGroupsOB.Add(ViewGroup);
                }
                else if (ViewGroupsOB.Count == 0)
                {
                    ViewGroupsOB.Add(ViewGroup);
                }

            }
        }
        await Task.Delay(5000);
    }
}

// ViewGroup의 ObservableCollection 선언
public ObservableCollection<ViewGroup> ViewGroupsOB { get => GetValue<ObservableCollection<ViewGroup>>(); set => SetValue(value); }

xaml에서 코드는 ViewGroup의 ObservableCollection을 ItemsSource로 Binding 하여 그 안에 ObservableCollection으로 데이터가 추가되는 ViewData를 실시간으로 업데이트 시키려고 구성하였습니다. 하지만 ViewModel에서 값을 집어 넣으면 처음 한 번은 값이 들어가지만 이후 ViewGroupOB의 값이 업데이트 되지 않아서 인지 제가 원했던 대로 동작을 하지 않더라고요 (당연한 얘기지만요…)
어떤 식으로 코드를 수정해야 {Binding ViewGroupOB} 를 통해서 UI 상에서 ViewData의 CH_Value를 계속해서 갱신시키고 차트와 같은 그래프를 그릴 수 있을까요…?

1개의 좋아요

작성해주신 코드에서는 메서드 진입 시 최초 1회만 데이터베이스에서 쿼리를 하도록 되어 있고, 최초에 ViewGroup이 존재하지 않을 경우만 ViewData를 추가하도록 되어 있습니다.

원하시는 동작을 하려면 아래와 같은 코드가 되어야 하지 않을까 합니다.
매 5초 루프마다 쿼리가 일어나며, ViewGroup이 존재하지 않을 때는 ViewGroup을 생성하고 조건문 밖에서 ViewData를 추가하도록 수정했습니다.

public async void ViewDataReNew()
{
    var ViewGroupsOB = new ObservableCollection<ViewGroup>();
    using (var newdbContext = new DataAccessEFCore())
    {
        var groupIds = new System.Collections.Generic.Dictionary<int, ViewGroup>(); // 새로운 Group 객체를 저장할 HasSet 생성

        var groupSelectionAll = newdbContext.SettingGroupConfigs
                                            .Include(i => i.SettingSingleConfigs)
                                            .ThenInclude(i2 => i2.DeviceChannel)
                                            .ThenInclude(i3 => i3.DeviceDatas).AsNonTracking();

        while (true)
        {
            foreach (var item in groupSelectionAll)
            {
                if (!groupIds.TryGetValue(item.Id, out var ViewGroup))
                {
                    ViewGroup = new ViewGroup
                    {
                        id = item.Id,
                        groupName = item.GroupName,
                        ViewData = new ObservableCollection<ViewData>()
                    };

                    groupIds.Add(item.Id, ViewGroup);
                    ViewGroupsOB.Add(ViewGroup);
                }

                foreach (var item2 in item.SettingSingleConfigs)
                {
                    var ViewDATA = new ViewData
                    {
                        DataId = item2.Id,
                        Number = item2.TypeNumber,
                        CH_Name = item2.DeviceChannel.DeviceChannelName,
                        CH_Value = item2.DeviceChannel.DeviceDatas.GroupBy(g => g.DeviceChannels.DeviceChannelId)
                                                                .Select(s => s.OrderByDescending(o => o.WriteDate).First())
                                                                .FirstOrDefault(w => w.DeviceChannels != null).CH_Value,
                        ViewGroups = ViewGroup
                    };

                    // 데이터 지속적으로 업데이트 하는 코드
                    ViewDATA.CH_Value = item2.DeviceChannel.DeviceDatas.GroupBy(g => g.DeviceChannels.DeviceChannelId)
                                                    .Select(s => s.OrderByDescending(o => o.WriteDate).First())
                                                    .FirstOrDefault(w => w.DeviceChannels != null).CH_Value;
                    ViewDATA.WriteDate = item2.DeviceChannel.DeviceDatas.GroupBy(g => g.DeviceChannels.DeviceChannelId)
                        .Select(s => s.OrderByDescending(o => o.WriteDate).First())
                        .FirstOrDefault(w => w.DeviceChannels != null).WriteDate;
                    //여기부터 수정

                    ViewGroup.ViewChannelData.Add(ViewDATA);
                }


            }
            await Task.Delay(5000);
        }
    }
}
3개의 좋아요

성심껏 달아주신 답글 감사합니다…!
알려주신 해당 방법은 시도해 보진 않았지만
코드 작성하다 깨달은 부분이 있어서 해결은 1차적으로 했습니다 .

제 해결 방법 같은 경우 ViewModel에서 db 로직을 돌리는 것이 아닌 Model Class를 선언한 곳에서 Function을 하나 만들어서 그 안에서 Data 로직을 처리하고 ViewModel에서는 해당 함수를 선언하는 방식으로 해결하였습니다 ㅎㅎ

1개의 좋아요