CameTooFar

A Nerd's False Positive.

Pass values between 2 MDI Child Forms using C# and VB.NET


Recently, I came across a situation where I need to pass value from one Child Form to another Child Form inside an MDI Parent. The scenario was like this. I have an MDI, whose MenuStrip pops out 2 Child Forms together. One child-form lists the Id and Name of all the Employees coming under certain criteria. On double clicking that particular employee, I need to display the value on the second child-form. In short, I need to pass the Id of the employee to the second child-form so that relevant details can be loaded. I’ll simulate the same scenario using a simple example.

Imagine I want to send a value from one of my Child form to display that in my second Child Form. The sample UI looks like this:

winform_pass_value_btween_mdi

[Send Value] –> ChildForm1.cs

   1: /// <summary>
   2: /// Send Value
   3: /// </summary>
   4: /// <param name="sender"></param>
   5: /// <param name="e"></param>
   6: private void button1_Click(object sender, EventArgs e)
   7: {
   8:     ChildForm2 formChild2 = (ChildForm2)this.MdiParent.MdiChildren[1];
   9:     formChild2.ReceiveValue(textBox1.Text);
  10: }
 

[Receive Value] –> ChildForm2.cs

   1: // Receive Value
   2: public void ReceiveValue(string value)
   3:         {
   4:             textBox1.Text = value;
   5:             this.Activate();
   6:             this.Refresh();
   7:         }

 

VB.NET


[Send Value] –> ChildForm1.vb


   1: Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   2:  
   3:         Dim formChild2 As ChildForm2 = DirectCast(Me.MdiParent.MdiChildren(1), ChildForm2)
   4:         formChild2.ReceiveValue(TextBox1.Text)
   5:  
   6:     End Sub

 

[Receive Value] –> ChildForm2.vb

   1: Public Sub ReceiveValue(ByVal value As String)
   2:        TextBox1.Text = value
   3:        Me.Activate()
   4:        Me.Refresh()
   5:    End Sub


Hope this helps.

Thanks

Cross-Thread operation not valid in WinForm


One problem while working with Threads in C# is that a background thread cannot ‘talk’ to the any visual elements that exists in the main GUI thread, because talking to objects across thread boundaries is forbidden. The reasons are obvious; because a thread has its own process space, the address of an object in one thread is not useful as an address in another thread. As a result, trying to access that object in another space results in a program crash with an unhandled exception. However, C# provides a mechanism for working with cross-thread process issue.

The BeginInvoke method of the MethodInvoker delegate is used within a thread prcoess to call another process method. These methods are thread-safe and can be used in a multi-threaded environment.

Last night, when a Thread tried to access the Text property of a Label control. I got the following error:

Cross-Thread operation not valid: Control ‘label3’ accessed from a thread other than the thread that it was created on.

Add the below quoted codes to perform a cross-thread operation.

   1: if (label3.InvokeRequired)
   2: {
   3:     label3.Invoke(new MethodInvoker(delegate{label3.Text = "sample text";});
   4: }


Hope this helped.

Thanks Winking smile

Why you should learn Delegates?


What everyone should learn while start writing code is the Basic Fundamentals, which most of them doesn’t do (myself among the one). Instead they go behind the attractive features, which is easy to learn and understand. In short run, you’ll win. But, in the long run, you’ll fail or you’ll end up using more lines of codes than required or you’ll write the most ugliest code.

I’m no different and I end up writing bloody-ugly code, due to my ignorance, delay and laziness to learn the fundamentals.

Few months back, I’m playing with user-controls in Silverlight 4, in which I want to pass data from one user control to another. And, delegate is the most perfect and standard way to do that.

The Ugly side of Ignorance – Object Passing

My situation was like this, I have a main page (say, MainPage.xaml) in which I’m loading a User Control (say, DocumentDetails.xaml). The MainPage.xaml contains a Label control which displays the total number of documents loaded in the  DocumentDetails.xaml.

Something like this:

WithoutDelegate

What I’d done is, in my DocumentDetails.xaml, I created a property named DocumentCount whose type is Label. The purpose of this property is to keep a reference of the Label in the MainPage.xaml, so that any update to the property: DocumentCount will update the original Label content in the MainPage.xaml.

Here goes my (ugly) code:

Documents.cs (Custom Class)

/*** Documents.cs ***/
 
public class Documents
    {
        public int Id { get; set; }
 
        public string Name { get; set; }
 
        public string Type { get; set; }
    }

 

DocumentDetails.xaml (Design)

<!-- DocumentDetails.xaml (Design) -->
<Grid x:Name="LayoutRoot" Margin="10" Height="Auto" Width="Auto" 
HorizontalAlignment="Center" Background="White">
        <sdk:DataGrid AutoGenerateColumns="False" 
                      Height="Auto" 
                      Margin="10"
                      RowHeight="40"
                      HorizontalAlignment="Left" 
                      Name="dgDocuments" 
                      VerticalAlignment="Top" 
                      Width="Auto">
            <sdk:DataGrid.Columns>
                <sdk:DataGridTemplateColumn Header="Id" Width="50">
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Id}" />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                </sdk:DataGridTemplateColumn>
                <sdk:DataGridTemplateColumn Header="Name" Width="150">
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Name}" />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                </sdk:DataGridTemplateColumn>
                <sdk:DataGridTemplateColumn Header="Type" Width="150">
                    <sdk:DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Type}" />
                        </DataTemplate>
                    </sdk:DataGridTemplateColumn.CellTemplate>
                </sdk:DataGridTemplateColumn>
            </sdk:DataGrid.Columns>
        </sdk:DataGrid>
    </Grid>

 

DocumentDetails.xaml.cs (Code-Behind)

/*** DocumentDetails.xaml.cs ***/
public partial class DocumentDetails : UserControl
    {
        /// <summary>
        /// Reference of Label object from MainPage.xml
        /// </summary>
        public Label DocumentCount { get; set; }
 
        /// <summary>
        /// Constructor
        /// </summary>
        public DocumentDetails()
        {
            InitializeComponent();
        }
 
        /// <summary>
        /// User Control Loaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            List<Documents> olstDocs = new List<Documents>() { 
                new Documents(){ Id = 1, Name = "Name 1", Type = "Type 1" },
                new Documents(){ Id = 2, Name = "Name 2", Type = "Type 2" },
                new Documents(){ Id = 3, Name = "Name 3", Type = "Type 3" },
                new Documents(){ Id = 4, Name = "Name 4", Type = "Type 4" },
                new Documents(){ Id = 5, Name = "Name 5", Type = "Type 5" }
            };
 
            // Set Count
            DocumentCount.Content = "Total Docs: " + olstDocs.Count;
 
            // Bind Data
            dgDocuments.ItemsSource = olstDocs;
        }
    }


MainPage.xaml (Design)

<!-- MainPage.xaml (Designer) -->
<Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Center">
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="40" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        
        <!-- Count -->
        <sdk:Label Grid.Row="0" 
                   Grid.Column="0" 
                   Name="lblCount" 
                   Content="Total Docs: " 
                   VerticalAlignment="Center" 
                   HorizontalAlignment="Left" 
                   VerticalContentAlignment="Center" 
                   Width="120" 
                   FontSize="12" />
 
        <!-- Load UserControl -->
        <Button Content="Load Documents" 
                Grid.Row="1" 
                Grid.Column="0"
                Height="23" 
                HorizontalAlignment="Left" 
                Name="btnLoadDocs" 
                VerticalAlignment="Center" 
                Click="btnLoadDocs_Click"
                Width="150" />
 
        <!-- Container -->
        <StackPanel Grid.Row="2" 
                    Grid.Column="0" 
                    x:Name="stkpnlContainer" 
                    Height="Auto" 
                    Width="400" 
                    HorizontalAlignment="Left" 
                    Orientation="Vertical" 
                    Background="Gray">
        </StackPanel>
        
    </Grid>

 

MainPage.xaml.cs (Code-Behind)

/*** MainPage.xaml.cs (Code-Behind) ***/
public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
        }
 
        /// <summary>
        /// Load Document Control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnLoadDocs_Click(object sender, RoutedEventArgs e)
        {
            DocumentDetails oDocDetails = new DocumentDetails();
            // Pass the Label reference
            oDocDetails.DocumentCount = lblCount;
 
            stkpnlContainer.Children.Clear();
            stkpnlContainer.Children.Add(oDocDetails);
        }
    }

 

So what’s the problem with this? It’ll work fine. Correct?

Well the problem is, I’m passing the object reference itself. And the situation is more worse, if I want to update more Controls in my parent form (here MainPage.xaml), which makes me to pass more control reference, in turn making it ugly.

The Powerful side of Fundamentals – Delegates

Well…Well…Well… There is a powerful solution to handle this called Delegates. Simply, Delegate is function pointer, the most common definition that you’ll hear, if you ask a good amount of developers! Another definition? They’ll scratch their head.

Windows and Web development relies on event-driven mechanism. When you perform an Action (Event, say Click), you need a Method/Procedure (Event Handler) to be invoked as part of the action, to perform an operation. And, Delegate acts as a conduit between Event Source and Event Handler methods. The delegate carries the information about the Event to the Event Handler.

In our situation, you can handle the total count displaying in the MainPage.xaml by;

  1. Declaring delegate & event in the DocumentsDisplay.xaml
  2. Registering event in the MainPage.xaml & defining the event-handler method to handle the raised event
  3. Here goes the code:
    DocumentDetails.xaml.cs
    /*** DocumentDetails.xaml.cs ***/
    public partial class DocumentDetails : UserControl
        {
            /// <summary>
            /// Delegate
            /// </summary>
            /// <param name="count"></param>
            public delegate void TotalCountHandler(int count);
     
            /// <summary>
            /// Event to fire the "Total Count" value
            /// </summary>
            public event TotalCountHandler Count;
     
            /// <summary>
            /// Constructor
            /// </summary>
            public DocumentDetails()
            {
                InitializeComponent();
            }
     
            /// <summary>
            /// User Control Loaded
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void UserControl_Loaded(object sender, RoutedEventArgs e)
            {
                List<Documents> olstDocs = new List<Documents>() { 
                    new Documents(){ Id = 1, Name = "Name 1", Type = "Type 1" },
                    new Documents(){ Id = 2, Name = "Name 2", Type = "Type 2" },
                    new Documents(){ Id = 3, Name = "Name 3", Type = "Type 3" },
                    new Documents(){ Id = 4, Name = "Name 4", Type = "Type 4" },
                    new Documents(){ Id = 5, Name = "Name 5", Type = "Type 5" }
                };
     
                // Set Count
     
                // Check whether event is registered or not
                if (Count != null)
                {
                    // Fire Event
                    Count(olstDocs.Count);
                }
     
                // Bind Data
                dgDocuments.ItemsSource = olstDocs;
            }
        }
    MainPage.xaml.cs
    /*** MainPage.xaml.cs ***/
    public partial class MainPage : UserControl
        {
            DocumentDetails oDocDetails;
     
            /// <summary>
            /// Constructor
            /// </summary>
            public MainPage()
            {
                InitializeComponent();
            }
     
            /// <summary>
            /// Load Document Control
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="e"></param>
            private void btnLoadDocs_Click(object sender, RoutedEventArgs e)
            {
                oDocDetails = new DocumentDetails();
                
                // Register Event
                oDocDetails.Count += new DocumentDetails.TotalCountHandler(oDocDetails_Count);
     
                stkpnlContainer.Children.Clear();
                stkpnlContainer.Children.Add(oDocDetails);
            }
     
            /// <summary>
            /// EventHandler method that sets the Total Count
            /// </summary>
            /// <param name="count"></param>
            void oDocDetails_Count(int count)
            {
                lblCount.Content = "Total Docs: " + count;
     
                // Remove Event
                oDocDetails.Count -= oDocDetails_Count;
            }
        }

Preview

Delegate

Download Source

Hope this helped. Thanks!