Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Load XAML Dynamically and Find Specified Control Using Name And Show Image, Play Video, and YouTube In WPF

5.00/5 (2 votes)
14 Jul 2020CPOL2 min read 8.4K   154  
Load XAML, find control, and play Youtube video automatically
This project shows that how to load custom XML file in your code, and find specified control using name that you want in runtime. And do show some image, play some video file, and auto play some Youtube video link. There are so many articles about these issues on the internet. But those sources are scattered and have some issues.

Using the Code

At first, make a WPF project.

Next, load XML file and load in window class.

C#
XmlTextReader xmlTextReader = new XmlTextReader(@".\LayoutResources\Main.xaml");
object instance = XamlReader.Load(xmlTextReader);

// add load event delegate
this.Loaded += MainController_Loaded;

this.Content = instance;

On upper codes, I make a 'Main.xaml' file. it, 'Main.xaml', file's build action property must be 'none' and copy to output directory property must be 'copy always' or 'copy it newer'.

I wrote an XAML like this:

XML
<Grid 
    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Grid.Background>
        <!--You should add '..\' and its location means your program's executed location.//-->
        <ImageBrush ImageSource="..\LayoutResources\Images\defaultBackground.png"></ImageBrush>
    </Grid.Background>
    <DockPanel LastChildFill="True">
        <Grid DockPanel.Dock="Top" Height="150">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="50"></RowDefinition>
                    <RowDefinition Height="*"></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="100"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <Button Name="btnExit" Background="Transparent" BorderThickness="0"></Button>
            </Grid>
        </Grid>
        <Grid DockPanel.Dock="Bottom" Height="100">
            <TextBlock Name="tblNotice" HorizontalAlignment="Center" 
                       VerticalAlignment="Center"></TextBlock>
        </Grid>
        <DockPanel LastChildFill="True" Margin="20">

            <Image Name="imgMain" Width="200" DockPanel.Dock="Left"></Image>

            <MediaElement Name="mpeMain" Width="200" DockPanel.Dock="Left"></MediaElement>
            
            <ContentControl Name="webMain"></ContentControl>
        </DockPanel>
    </DockPanel>
</Grid>

As upper, I used Image, MediaElement, and ContentControl controls. ContentControl is used for Chrome WebBrowser which plays a Youtube video. You already know why I should have used Chrome instead of default web browser provided in WPF. It has some issues on about JavaScript and compatibilities. If you insist on using it, you will face a lot of stress.

So to use Chrome, please install like the below code in nuget package console.

install-package cefsharp.common <enter>

install-package cefsharp.wpf <enter>

And then, add codes like below in MainController constructor function. It is related to the latest Google policy.

If you want to play Youtube, you should do this:

C#
// Chrome web browser setting for auto play
CefSharp.Wpf.CefSettings settings = new CefSharp.Wpf.CefSettings();
if (settings.CefCommandLineArgs.ContainsKey("autoplay-policy"))
    settings.CefCommandLineArgs["autoplay-policy"] = "no-user-gesture-required";
else
    settings.CefCommandLineArgs.Add("autoplay-policy", "no-user-gesture-required");
CefSharp.Cef.Initialize(settings);

After that, fill codes in 'MainController_Loaded' function.

C#
// find button named 'btnExit'
Button btn = FindControl<Button>(this, "btnExit");
if (btn != null)
{
    btn.Content = "Click Me";
    btn.Click += btn_Click;
}

// find textblock named 'tblNotice'
TextBlock tblNotice = FindControl<TextBlock>(this, "tblNotice");
if (tblNotice != null)
{
    tblNotice.Text = "Hi";
    tblNotice.FontSize = 20;
}

Image imgMain = FindControl<Image>(this, "imgMain");
if(imgMain != null)
{
    // this below is a way find absolute path
    // this file's build action property must be 'none' 
    // and copy to output diectory property must be 'copy always' or 'copy it newer'.
    String strPath = String.Format(
        "{0}LayoutResources\\Images\\defaultImage.jpg",
        AppDomain.CurrentDomain.BaseDirectory);
    imgMain.Source = new BitmapImage(new Uri(strPath, UriKind.Absolute)); 
}

MediaElement mpeMain = FindControl<MediaElement>(this, "mpeMain");
if(mpeMain != null)
{
    mpeMain.LoadedBehavior = MediaState.Manual;
    mpeMain.MediaEnded += mpeMain_MediaEnded;

    // this below is a way find absolute path
    // this file's build action property must be 'none' 
    // and copy to output diectory property must be 'copy always' or 'copy it newer'.
    String strPath = String.Format(
        "{0}LayoutResources\\Images\\defaultMedia.mp4",
        AppDomain.CurrentDomain.BaseDirectory);

    mpeMain.Source = new Uri(strPath, UriKind.Absolute);
    mpeMain.SpeedRatio = 1;

    // start
    mpeMain.Play();
}

ContentControl webMain = FindControl<ContentControl>(this, "webMain");
if (webMain != null)
{
    String strUrl = "https://www.youtube.com/embed/LGxT88Ljdc4" + "?autoplay=1";
    webMain.Content = new ChromiumWebBrowser(strUrl);
}

As you will understand the codes, the point is FindControl function. This is a function finding a control that you want.

Let's tell about the finding control. This code can be used when you want to find a control that you want with name in WPF. There are so many useful articles about this issue on the internet. I also found useful sources there. Without them, I couldn't do anything. I referred to this article 'How can I find WPF controls by name or type?' But it is has one flawless point. An original code is simple. See below:

C#
/// <summary>
/// Finds a Child of a given item in the visual tree. 
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter. 
/// If not matching item can be found, 
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
   where T : DependencyObject
{    
  // Confirm parent and childName are valid. 
  if (parent == null) return null;

  T foundChild = null;

  int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
  for (int i = 0; i < childrenCount; i++)
  {
    var child = VisualTreeHelper.GetChild(parent, i);
    // If the child is not of the request child type child
    T childType = child as T;
    if (childType == null)
    {
      // recursively drill down the tree
      foundChild = FindChild<T>(child, childName);

      // If the child is found, break so we do not overwrite the found child. 
      if (foundChild != null) break;
    }
    else if (!string.IsNullOrEmpty(childName))
    {
      var frameworkElement = child as FrameworkElement;
      // If the child's name is set for search
      if (frameworkElement != null && frameworkElement.Name == childName)
      {
        // if the child's name is of the request name
        foundChild = (T)child;
        break;
      }

      /************************* this part *************************/

    }
    else
    {
      // child element found.
      foundChild = (T)child;
      break;
    }
  }

  return foundChild;
}

You can see this code in the article 'How can I find WPF controls by name or type?'. Please see the point that I mentioned, '/** this part **/'

You should see more code in there. If you find the same type of control, but it is not what you want, this code will skip to find its children. Even if it also has children, that is the point.

So, I added some code in there, see the below codes.

An added code in /************************* this part *************************/

C#
// recursively drill down the tree                             
foundChild = FindChild<T>(child, childName);                             

// If the child is found, break so we do not overwrite the found child.    
if (foundChild != null) break;

With the upper code, you can find the control wherever it is. See the full code I changed like below:

C#
public T FindControl<T>(DependencyObject objParent, String strName) where T : DependencyObject
{
    // Check parent and name
    if (objParent == null || String.IsNullOrEmpty(strName)) return null;

    T existedChild = null;

    int childrenCount = VisualTreeHelper.GetChildrenCount(objParent);
    for (int i = 0; i < childrenCount; i++)
    {
        var child = VisualTreeHelper.GetChild(objParent, i);

        // Check if the child is not the same type with a thing that you want
        if ((child as T) == null)
        {
            // Find control more in it
            existedChild = FindControl<T>(child, strName);

            // If the control is existed in it, there is nothing to proceed. 
            if (existedChild != null) break;
        }
        else
        {
            var frameworkElement = child as FrameworkElement;
            if (frameworkElement != null)
            {
                // If this control's name is the same that you want
                if (!String.IsNullOrEmpty(frameworkElement.Name) &&
                    frameworkElement.Name == strName)
                {
                    existedChild = (T)child;
                    break;
                }
                // Important!. If it is not the same name that you want, 
                // you also should find more in its childs recursively.
                else
                {
                    // Find control more in it
                    existedChild = FindControl<T>(child, strName);

                    // If the control exists in it, there is nothing to proceed. 
                    if (existedChild != null) break;
                }
            }
        }
    }

    return existedChild;
}

You will download that project I uploaded. Enjoy it!

Points of Interest

Load XAML, find control, and play Youtube video automatically

History

  • 15th July, 2020: First issued

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)