Truy cập một control trong một form từ một form khác trong wpf

Em có một câu hỏi nhờ các anh chị giúp đỡ. Em có một control wrappanel(x:Name wpPrọ) trong formmain, trong một form1 khác em có một một cái button1. Bây giờ phải làm sao để em ở form1 khi xử lí sự kiện cho button1 em có thể gọi thằng wpPro này để xử lý. Em đã thử khởi tạo một thể hiện của Mainform rồi truy cập nó thông qua thể hiện này nhưng khi chạy bấm button nó chả xử lí được gì, thằng wrappanel nó không thay đổi gì. Em đã search "How can I access a control in WPF from another class " Nhưng vẫn chưa làm được.

*Đây là cách hiểu của em khi giải quyết vấn đề này nếu có sai mong anh chị các bạn cứ chọi đá. Khi ta design form trong xaml đó chỉ là một “mẫu”, “lớp” khi ta chạy nó khởi tạo đối tượng từ lớp đó. Giờ muốn thay đổi các control trong đối tượng đó phải tham chiếu đến đối tượng đó. Giờ phải làm sao đây ạ, con trỏ có làm ăn được gì không. Bất kỳ lời khuyên nào có thể. Xin cám ơn?

Cách thứ nhất (không theo quy tắc):
Là bạn phải làm thế nào để cho thằng form 2 nó biết cái control muốn truy cập là thằng nào hoặc thằng chứa control đó là thằng nào.

Cách làm là ở thằng form2 khai báo 1 biến là loại control của thằng mainform. Hoặc khai báo 1 biến loại mainform (chứa control). Trước khi show cái form2 thì gán biến đó của form2 là control của mainform hoặc gán trực tiếp mainform vào. Rồi trong form2 sẽ tương tác thông qua biến đó. Cách này dễ hiểu.

Cách 2 là dùng delegate. Hiểu một cách trừu tượng là giữa 2 form hình thành một kết nối dữ liệu. Form2 đẩy dữ liệu qua rồi uỷ quyền cho thằng mainwindow thực hiện xử lý tiếp dữ liệu.

Cách 3 là dùng databinding. Nôm na là cả 2 thằng cùng sử dụng chung 1 dữ liệu. Khi thằng này thay đổi dữ liệu thì những thằng khác cũng biết và tự động cập nhật.

2 Likes

Cách thứ 2 xin hỏi tác giả có thể code ví dụ cho mình không, vì mình được học delegate Delegate là một biến kiểu tham chiếu mà giữ tham chiếu tới một phương thức. Tham chiếu đó có thể được thay đổi tại runtime. Đặc biệt, các delegate được sử dụng để triển khai các sự kiện và các phương thức call-back. Mình không nhìn ra được là là nó làm sao để gọi một control có moddifier là private trong formmain.
Cách thứ 3. Nếu bin dingf ở mức thấp là thám chiếu dữ liệu từ thằng này qua thằng kia( 2 thằng nhìn nhau) thì tác giả đã hiểu nhầm ý mình. Ý mình là mình gọi con control wrappanel trong sự kiện button của form1 để xử lí, cụ thể ở đây mình muốn add một cái usercontrol hoặc một cái button, label … Còn databinding ở mức cao thì mình chưa biết nhiều.
Cách thứ 1 thực sự mình quá mơ hồ. Có code example
thì hay.

Một cách mình đã làm được: https://stackoverflow.com/questions/17001486/how-to-access-wpf-mainwindow-controls-from-my-own-cs-file
Ở đây họ bày 3 cách một cách khá, nhanh nhưng không chuyên nghiệp. một cách sử đụng thuần MVVM khó hơn. Cám ơn các bạn.

Ở cách 1:
MainWindow có 1 cái WrapPanel như sau:

<WrapPanel Name="wrpDemo"/>

Ở Window2 sẽ khai báo một cái WrapPanel gán bằng null.

public partial class Window2 : Window{
     public WrapPanel RemotePanel = null;
}

Bây giờ tại cái nơi gọi showw Window2, thì gán cái RemotePanel của Window2 bằng cái WrapPanel của MainWindow. Rồi sau đó mới show Window2 lên.

Window2 wd = new Window2();
wd.RemotePanel = this.wrpDemo;
wd.Show();

Và bây giờ cái RemotePanel trong Window2 nó đang trỏ tới cái wrpDemo của MainWindow, nên muốn làm gì với wrpDemo thì chỉ cần làm nó với RemotePanel.

Cũng với cách trên thì có thể truyền luôn cả MainWindow qua. Nhưng wrpDemo phải set public.
Việc gán control qua có thể gán sau khởi tạo như code trên hoặc gán trong constructor của Window2.

1 Like

Cách 2 dùng delegate hoặc event thực chất là không phải truy cập trực tiếp vào WrapPanel của MainWindow để add control vào. Mà chỉ đơn thuần là chuyển những dữ liệu cần thiết từ Window2 qua và ủy quyền cho MainWindow thực hiện việc thêm xóa gì đó.
Nếu không đúng yêu cầu truy cập trực tiếp vào control thì có thể bỏ qua cách này.

1 Like

Ở cách 3, thực chất là cũng không truy cập trực tiếp đến WrapPanel. Mà WrapPanel sẽ binding Item của nó tới 1 danh sách Item kiểu Control. Trên Window2 chỉ cần tương tác thêm xóa Item trong danh sách thì WrapPanel trên MainWindow cũng sẽ tự động update.

Ta sẽ có 1 danh sách Item loại Control. ObservableCollection nằm trong System.Collections.ObjectModel. Control nằm trong System.Windows.Controls. Khai báo này để trong file cs nào cũng được. Yêu cầu là public static để truy cập được ở mọi nơi.

public static ObservableCollection<Control> Items = new ObservableCollection<Control>();

WrapPanel của MainWindow sẽ thiết kế thế này:

<WrapPanel Name="wrpDemo" Margin="0,0,222,0">
      <ItemsControl Name="Items"/>
</WrapPanel>

Ở đâu đó trong MainWindows sẽ set ItemSource của Items là danh sách Items vừa tạo. Ở đây mình để luôn trong constructor. Danh sách Items mình đặt trong class riêng tên là BindingItems.

Items.ItemsSource = BindingItems.Items;

Bây giờ show Window2 bình thường.

Window2 wd = new Window2();
wd.Show();

Ở Window2 hay bất kỳ đâu muốn thêm xóa control trên WrapPanel của MainWindow chỉ cần tương tác với danh sách BindingItems.Items.

BindingItems.Items.Add(new Button(){Width=100,Height=25, Content="Hehe"}); // thêm một button vào danh sách
BindingItems.Items.RemoveAt(1); // xóa control có chỉ số = 1 trong danh sách

Cách binding này không theo MVVM. Nếu theo MVVM sẽ viết dài hơn khá nhiều.

1 Like
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?