MVVM 패턴 TabControl과 TabItem DataContext 분리

public partial class A: Grid
{
public A()
{
InitializeComponent();
DataContext = new MainViewModel();
}
public class MainViewModel
{
public B G1VM { get; set; } = new B();
public C G2VM { get; set; } = new C();
}
}

ViewModel

    static public ObservableCollection<Table> datagrid{ get; set; } = new ObservableCollection<Table>();   

public C()
{
datagrid.Add(new Table{
Name= “Hello”
};
}

다음과 같이 MVVM 구조에서 총 3개의 View Model의 DataContext를 설정하였습니다.

  <TabControl   DataContext="{Binding G1VM}">

그렇게 한 뒤에 TabControl에 G1VM을 Binding한 뒤에,

<TabItem Header=“Tmp”
DataGrid ItemsSource="{Binding datagrid, UpdateSourceTrigger=PropertyChanged}" DataContext ="{Binding G2VM}"

<DataGrid.Columns>
<DataGridTemplateColumn Header=“TmpName”
<DataGridTemplateColumn.CellTemplate
<DataTemplate
<TextBox Text="{Binding Name}"
</DataTemplate
</DataGridTemplateColumn.CellTemplate
</DataGridTemplateColumn
</DataGrid.Columns>
다음과 같이 TabItem 내부의 DataGrid만 G2VM으로 바인딩을 진행하고 싶습니다.
하지만 DataGrid가 이미 G1VM으로 바인딩이 돼버리는데, 이 부분을 어떻게 해결할 수 있을까요?
datagrid는 ObservableCollection 자료형입니다.

좋아요 3

@미니뀨 안녕하세요 :smile:

Binding RelativeSource를 사용하시면 됩니다.

<TabControl DataContext="{Binding G1VM}">
    <TabItem Header="TMP">
        <DataGrid ItemsSource="{Binding datagrid}" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.G1VM}"/>
    </TabItem>
</TabControl>

저도 정말 자주 사용하는 부분 중에 하나에요.
특히 CommandParameter를 사용할 때 자주 상위 객체의 DataContext에 접근하기 떄문에요~

그리고 제가 정리해놓은 Binding 관련 내용도 한번 참고해보시길 바랍니다.
여기

좋아요 1

음 이상하게 해결이 되지 않네요 ㅠㅠ… 제가 datagrid를 Static 변수로 설정했는데 이게 영향이 있는 걸까요?

좋아요 1

@미니뀨 혹시 본문에 소스코드를 좀더 보완해주실 수 있으실까요?

그리고 datagrid ObservableCollection<T>는 G2VM 클래스 안에 public get; set; 형태로 있어야 합니다.

public class G2VM
{
    public ObservableCollection<T> datagrid { get; set; }
}

해보시고 혹시 필요하시면 간단한 샘플 만들어 드릴께요. :smile:

좋아요 1

소스코드를 조금 더 보완했는데 충분하실 지 모르겠네요 ㅠㅠ

코드가 이상하게 닫으면 보이지 않아 가독성이 떨어집니다… 죄송합니다.

Class B는 DataGrid를 제외한 나머지에 대한 ViewModel이며, Class C는 DataGrid 용의 ViewModel입니다.

좋아요 1

괜찮습니다. :smile:
Markdown을 배워보시면 질문하실 때 소스코드나 가독성을 훨씬 키우실 수 있어요. 여기


샘플 소스코드 만들어드렸으니 한번 확인해보시길 바랍니다.

샘플코드 여기


아래는 샘플 이미지입니다.

image

<Grid Background="{Binding GridBackground}">
	<TabControl DataContext="{Binding G1VM}" Background="{Binding TabControlBackground}">
		<TabItem Header="Tab Item 1">
			<DataGrid ItemsSource="{Binding Users}" DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.G2VM}"/>
		</TabItem>
	</TabControl>
</Grid>
좋아요 3

도와주셔서 감사합니다 ㅠㅠ 덕분에 문제가 풀렸습니다 복받으세요!!!

좋아요 2

Binding는 DataContext의 상대적 경로를 따르는 것으로 알고 있습니다.

예를 들어 Grid에 DataContext 바인딩 했으면 Grid 내부에 있는 아이템은 Grid와 연결된 바인딩 데이터로부터 아이템을 찾는 것으로 알고 있습니다.

<Grid DataContext="{Binding SomeGridVM}">
   <TabControl DataContext="{이곳은 SomeGridVM 영향을 받습니다}"/>
</Grid>

이를 해소하기 위해서는 작업하는 코드의 기존에 사용하던 VM을 찾아줘야 하는데
그 방법이 @james.lee 님께서 알려주신 방법입니다.

또 다른 방법으로는 G1VMParent 속성을 추가하는 것 입니다.
이 방식을 사용하면 RelativeSource를 사용하지 않고 기존 VM에 접근할 수 있습니다.

<TabControl DataContext="{Binding G1VM}">
    <TabItem Header="TMP">
        <DataGrid ItemsSource="{Binding datagrid}" DataContext="{Binding Parent.G2VM}"/>
    </TabItem>
</TabControl>
좋아요 3

@level120 감사합니다 :smile:

저도 유용하게 사용할 수 있을 것 같습니다!

좋아요 1