Search code examples
uwp-xamlwinui-3winui

Same DataTemplate for different view


I have view1.xaml:

<DataTemplate x:Key="Item" x:DataType="viewmodels:itemVM">
    <Grid>
          <TextBlock Text="{x:Bind text, Mode=OneWay}"
                            Loaded="Text_Loaded" />
    </Grid>
</DataTemplate>
<ListView x:Name="listview"
          ItemTemplate="{StaticResource Item}"/>

If I have view2.xaml using the same DataTemplate of view1:

<DataTemplate x:Key="Item" x:DataType="viewmodels:itemVM">
    <Grid>
          <TextBlock Text="{x:Bind text, Mode=OneWay}"
                            Loaded="Text_Loaded" />
    </Grid>
</DataTemplate>
<FlipView x:Name="flipview"
          ItemTemplate="{StaticResource Item}"/>

But I don't want to duplicate the code in view (DataTemplate) as well as the code-behind(for Loaded="Text_Loaded"), so what is the solution for this?

Thanks


Solution

  • You could put the DataTemplate into a ResourceDictionary File, when you need to use it, just add the ResourceDictionary File to the Resources of the page.

    Also, you could bind the loading event to a command of the ViewModel. Then you don't need to write the event too many times.

    I made a simple demo here and you could follow it. Here is the steps first:

    1. Create a new ResourceDictionary File in your project, name it whatever you want. Put the DateTemplate into the ResourceDictionary File.

    2. Add the reference of ResourceDictionary File to the App.Xaml, then you could use it anywhere in your app.

    3. In the Solution Explorer panel, right click on your project name and select Manage NuGet Packages. In the NuGet Package Manager, select the Browse tab and search for Microsoft.Xaml.Behaviors.Uwp.Managed.Then install it.

    4. Change the DataTemplate with Xaml behavior so that you could bind the loaded event to a command of the ViewModel.

    Here is the code

    Dictionary1.xaml - the ResourceDictionary File

    <ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:local="using:TestTemplate"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
    xmlns:Interactions="using:Microsoft.Xaml.Interactions.Core">
    
    <DataTemplate x:Key="Item">
        <Grid>
            <TextBlock x:Name="Block" Text="{Binding text, Mode=OneWay}">
                 <Interactivity:Interaction.Behaviors>
                     <!--when the event triggers, call the command of the ViewModel-->
                     <Interactions:EventTriggerBehavior EventName="Loaded">
                         <Interactions:InvokeCommandAction Command="{Binding VM_Loaded}" CommandParameter="{Binding ElementName=Block}"/>
                     </Interactions:EventTriggerBehavior>
                 </Interactivity:Interaction.Behaviors>
            </TextBlock>
        </Grid>
    </DataTemplate>
    

    App.Xaml:

        <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
    

    MainPage.xaml:

     <Grid>
        <ListView x:Name="listview" ItemTemplate="{StaticResource Item}"/>
    </Grid>
    

    ViewModel:

       public class itemVM
    {
    
        public string text { get; set; }
    
        public ICommand VM_Loaded
        {
            get
            {
                return new CommadEventHandler<TextBlock>((s) => this.loaded(s));
            }
        }
    
        private void loaded(TextBlock element)
        {
            try
            {
                element.Text = "Test";
                Debug.WriteLine("loaded");
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }
    }
    
    
    public class CommadEventHandler<T> : ICommand
    {
        public event EventHandler CanExecuteChanged;
    
        public Action<T> action;
        public bool CanExecute(object parameter)
        {
            return true;
        }
    
        public void Execute(object parameter)
        {
            this.action((T)parameter);
        }
        public CommadEventHandler(Action<T> action)
        {
            this.action = action;
    
        }
    }