하나의 통신클래스에서 다른 객체들 접근 문의드립니다.

안녕하세요. 아발로니아로 프로그램을 개발하다 사용자와 컨셉이 맞지 않아 UI를 전면 수정하고 있습니다.

평소대로 클래스를 넘겨 개발 하려다 다른 방법이 있나해서 문의드립니다.

MainWindow에 아래 사진과 같이 A,B,C,D 의 유저컨트롤이 있고 각 유저컨트롤의 데이터는 H 라는 클래스에서 제공하고 있습니다.

H 클래스에서 이벤트 발생시 각각의 정보를 A,B,C,D 뷰모델로 보내어 데이터를 갱신해야 해서 H 클래스 생성시 MainWindow의 뷰모델을 넘겨 처리 함수들을 쭉 나열하고 아래 소스처럼 이벤트 실행시 해당 함수를 호출하도록 했는데

//H 클래스 생성시 MainWindowViewModel을 넘겨받음
public H ( MainWindowViewModel view) {MainViewModel = view;}

H클래스에서 A 유저컨트롤에 데이터 업데이트시 함수에 값을 넘겨줌
MainViewModel.setAData(value);

//MainWindowViewModel 함수

public void setAData(string value)
{
((AViewModel) MainWindow.A.DataContext).Data = value;
}

이런식으로 만들다 보니 MainViewModel에 모든 처리 함수들이 집중되어
뭔가 분산이 안된 느낌인데…

제일 좋은 방법이 H 클래스에서 각각의 UserControl의 ViewModel에 접근하는 건데… 제 수준에서는 ViewModel들을 전역으로 올려서 접근하는거 말고는 떠오르는게 없습니다. 가장 이상적인 접근 방법이 무엇이 있을까요??

2개의 좋아요

안녕하세요
저는 보통 데이터 관리를 ServiceProvider나 Manager 같은 싱글톤 클래스에서 하게 하고 ViewModel에 주입해서 사용하는 걸 선호합니다.
말씀해주신 부분에서는 H클래스가 비슷한 역할을 할 수 있을 것으로 보이네요.

ViewModel은 ReadOnlyCollection<T>, ObservableCollection<T>등으로 데이터 바인딩만 수행하고 값은 외부 Service나 Manager에서 변경되어서 들어오는 방식을 활용하고 있습니다. 더 나은 접근 방식이 있다면 저도 피드백 부탁드립니다!

2개의 좋아요

제가 이해하기로는 A,B,C,D는 보여주기만 하는 것으로 이해했습니다.
H는 데이터의 공급, Update를 하는 것으로 이해했습니다.

AvaloniaUI라고 하시니까 아마도 제일 쉬운 것은 CommunityToolkit.Mvvm을 통한 Messenger를 이용한 방법이 있을 것이고

다른 하나는 ReactiveX을 통해 H의 구독을 노출하고, A,B,C,D에서 구독을 연결하는 것이 있을 것 같습니다.

2개의 좋아요

보여 주신 UI 라면, 의존성 그래프는 아래와 같습니다.

AView → AViewModel → H
BView → BViewModel → H
CView → CViewModel → H
DView → DViewModel → H

의존성은 아래의 코드로 나타납니다.

class AViewModel
{
   H _h;
}

그런데, 질문 코드는 의존성의 설정이 거꾸로 되어 있습니다.

이러한 코드가 나타난 원인은 H가 "모델"인지 "서비스"인지 혼동했기 때문인 것 같습니다. H 가 모델이라면, H를 런타임에 제공할 서비스가 필요하고,

interface IHService
{
   H Get(int hId);
   IEnumerable<H> GetAll();
};

이 서비스는 뷰모델이나, 뷰가 의존하게 됩니다.

class AViewModel(IHService service) { }

이런 경우에도, 아래의 의존 관계는 잘못된 설정입니다.

IHService → AViewModel

참고로 H 가 모델이라면, 외부에게 상태 변화를 알리기 위해 이벤트를 적용하는 게 가장 간편합니다.

class H
{
   public event EventHandler<double> SensorChanged;
   public double Sensor { get; protected set; }
}

class AViewModel
{
   H _h;
   public AViewModel()
   {
      _h.SensorChanged += OnSensorChanged;
   }
   void OnSensorChanged(object? sender, double value)
   {
      // ...
}

위와 같이 고유 형식의 이벤트(EventHandler<double>)를 사용할 수도 있고, WPF의 데이터 바인딩 시스템에 의존할 수도 있습니다.

데이터 바인딩 시스템은 데이터 소스가 PropertyChangedEventHandler를 가질 것을 요구합니다. (INPC 구현을 통해)

class H : INotifyPropertyChanged
{
   public PropertyChangedEventHandler PropertyChanged { get; }
   // ...

이벤트를 변경하는 것만으로도, UI로의 데이터 반영은 데이터 바인딩 시스템이 알아서 해줄 것입니다.

5개의 좋아요

H 를 단독 쓰래드로 하고
옵저버 패턴써서 뷰모델 객체 에 던지던 Messenger 쓰던 하면 되지 않을까요

2개의 좋아요

가장 간단하게 수정 가능한 방향을 그려봤습니다.

현재 클래스 구조

문제점: MVVM 위반 및 의존 관계 오류

  • MainWindowViewModelMainWindow, A, B, C, D를 알고 있으며, View의 요소(A.DataContext 등)에 직접 접근
  • 서비스 성격의 HMainWindowViewModel을 알고 있음

개선된 구조

개선사항

  • MainWindowViewModel에서 AViewModel, BViewModel, CViewModel, DViewModel 을 참조
  • MainWindowViewModelH를 참조하고 데이터 변경을 이벤트 방식으로 변경. MainWindowViewModel에서 이벤트를 수신하여 AViewModel, BViewModel, CViewModel, DViewModel의 값 지정
8개의 좋아요

우와 도식화까지 한눈에 딱 들어오네요 ㅎㅎ 너무 감사드립니다~! 패턴을 최대한 지켜보려고 하는데 거참 어렵습니다. 알려주신 정보와 윗분들의 해결법을 활용해서 최대한 개발해보도록 하겠습니다.

2개의 좋아요

옵저버 패턴이 뭔지 몰라서 일단 메세지로 테스트를 해봤습니다. 옵저버는 후에 검색을 해봐야겠습니다. 감사합니다^^

1개의 좋아요

개발패턴에 대한 이해가 많이 부족합니다 ㅠㅠ H가 통신을 담당하는 객체라서 백그라운드 쓰레드처럼 독립적으로 실행되어야 한다는게 머리에 딱 박혀있어서 말씀대로 의존성을 거꾸로 설정 했나봅니다. 그런데 사실 진짜 독립적인건 이벤트든 메세지든 어떠한 연결고리 없이 통신을 하는거 였는데 말이죠. 너무 감사합니다.

2개의 좋아요

메세지 방식을 통해서 Test를 해봤습니다. 꼭 Mqtt 통신방법과 유사한거 같네요. 깊이 파고 들어가면 복잡하겠지만 약한참조로 하니 너무 간단하게 해결되네요 감사합니다!

1개의 좋아요

핫…독이 든 사과랄까요…

지금 툴킷의 메세지 방식을 적용해보고 있는데 복잡한 설정없이 구독?만 하면되는 구조라서 편하네요 ㅎ

2개의 좋아요

엇!! 어떠한 독일까요?? 누수 이야기가 있는거 같아서 주말내 테스트를 해봤지만 아직까진 괜찮은데… 치명적인 독이라면 방식을 바꿔야지요~~

1개의 좋아요

누수와 별개의 문제로

메신저 편하네 ~ 이쪽에다가 구현해야지 저쪽에다가 구현해야지~ 남발 할 경우에 대한 문제라서요.

(사실 뭐 … 이런게 문제였다면 아예 애초에 만들지 않았겠죠 :slight_smile: )