예제 #1
0
        public ViewManager(ItemsRepeater owner)
        {
            // ItemsRepeater is not fully constructed yet. Don't interact with it.

            m_owner     = owner;
            m_resetPool = new UniqueIdElementPool(owner);

            m_lastFocusedElement        = owner;
            m_phaser                    = new Phaser(owner);
            m_ElementFactoryGetArgs     = new ElementFactoryGetArgs();
            m_ElementFactoryRecycleArgs = new ElementFactoryRecycleArgs();
        }
예제 #2
0
        protected override UIElement GetElementCore(ElementFactoryGetArgs args)
        {
            if (m_templates is null || m_templates.Count == 0)
            {
                throw new InvalidOperationException("Templates property cannot be null or empty.");
            }

            var winrtOwner  = args.Parent;
            var templateKey =
                m_templates.Count == 1 ? m_templates.First().Key : OnSelectTemplateKeyCore(args.Data, winrtOwner);

            if (string.IsNullOrEmpty(templateKey))
            {
                // Note: We could allow null/whitespace, which would work as long as
                // the recycle pool is not shared. in order to make this work in all cases
                // currently we validate that a valid template key is provided.
                throw new InvalidOperationException("Template key cannot be empty or null.");
            }

            // Get an element from the Recycle Pool or create one
            var element = m_recyclePool.TryGetElement(templateKey, winrtOwner) as FrameworkElement;

            if (element is null)
            {
                // No need to call HasKey if there is only one template.
                if (m_templates.Count > 1 && !m_templates.ContainsKey(templateKey))
                {
                    string message = "No templates of key " + templateKey + " were found in the templates collection.";
                    throw new InvalidOperationException(message);
                }

                var dataTemplate = m_templates[templateKey];
                element = dataTemplate.LoadContent() as FrameworkElement;

                // Associate ReuseKey with element
                RecyclePool.SetReuseKey(element, templateKey);
            }

            return(element);
        }
        protected override UIElement GetElementCore(Microsoft.UI.Xaml.Controls.ElementFactoryGetArgs args)
        {
            if (args.Data == null)
            {
                return(null);
            }
            Type dataType = args.Data.GetType();

            if (dataType == typeof(int))
            {
                return(new Button()
                {
                    Content = args.Data,
                    Tag = args.Data,
                    Style = (Style)App.Current.Resources["NumberPanelButtonStyle"],
                });
            }
            else
            {
                (args.Data as FrameworkElement).MinWidth = (double)App.Current.Resources["NumberPanelButtonWidth"];
                return((UIElement)args.Data);
            }
        }
        protected override UIElement GetElementCore(ElementFactoryGetArgs args)
        {
            object GetContent(IElementFactoryShim itemTemplateWrapper)
            {
                if (itemTemplateWrapper != null)
                {
                    return(itemTemplateWrapper.GetElement(args));
                }
                return(args.Data);
            }

            var newContent = GetContent(m_itemTemplateWrapper);

            // Element is already a RadioButton, so we just return it.
            var radioButton = newContent as RadioButton;

            if (radioButton != null)
            {
                return(radioButton);
            }

            // Element is not a RadioButton. We'll wrap it in a RadioButton now.
            var newRadioButton = new RadioButton();

            newRadioButton.Content = args.Data;

            // If a user provided item template exists, we pass the template down to the ContentPresenter of the RadioButton.
            var itemTemplateWrapper = m_itemTemplateWrapper as ItemTemplateWrapper;

            if (itemTemplateWrapper != null)
            {
                newRadioButton.ContentTemplate = itemTemplateWrapper.Template;
            }

            return(newRadioButton);
        }
예제 #5
0
        // There are several cases handled here with respect to which element gets returned and when DataContext is modified.
        //
        // 1. If there is no ItemTemplate:
        //    1.1 If data is a UIElement . the data is returned
        //    1.2 If data is not a UIElement . a default DataTemplate is used to fetch element and DataContext is set to data**
        //
        // 2. If there is an ItemTemplate:
        //    2.1 If data is not a FrameworkElement . Element is fetched from ElementFactory and DataContext is set to the data**
        //    2.2 If data is a FrameworkElement:
        //        2.2.1 If Element returned by the ElementFactory is the same as the data . Element (a.k.a. data) is returned as is
        //        2.2.2 If Element returned by the ElementFactory is not the same as the data
        //                 . Element that is fetched from the ElementFactory is returned and
        //                    DataContext is set to the data's DataContext (if it exists), otherwise it is set to the data itself**
        //
        // **data context is set only if no x:Bind was used. ie. No data template component on the root.
        UIElement GetElementFromElementFactory(int index)
        {
            // The view generator is the provider of last resort.
            var data = m_owner.ItemsSourceView.GetAt(index);

            UIElement GetElement()
            {
                var elementFactory = m_owner.ItemTemplateShim;

                if (elementFactory == null)
                {
                    if (data is UIElement dataAsElement)
                    {
                        return(dataAsElement);
                    }
                    else
                    {
                        // If no ItemTemplate was provided, use a default
                        //var factory  = XamlReader.Load("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>") as DataTemplate;
                        var factory = new DataTemplate(() =>
                        {
                            var tb = new TextBlock();
                            tb.SetBinding(TextBlock.TextProperty, new Binding());
                            return(tb);
                        });
                        m_owner.ItemTemplate = factory;

                        elementFactory = m_owner.ItemTemplateShim;
                    }
                }

                if (m_ElementFactoryGetArgs == null)
                {
                    m_ElementFactoryGetArgs = new ElementFactoryGetArgs();
                }

                var args = m_ElementFactoryGetArgs;

                using var scopeGuard = Disposable.Create(() =>
                {
                    args.Data   = null;
                    args.Parent = null;
                });

                args.Data   = data;
                args.Parent = m_owner;
                args.Index  = index;

                return(elementFactory.GetElement(args));
            };

            var element = GetElement();

            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);

            if (virtInfo == null)
            {
                virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
                REPEATER_TRACE_PERF("ElementCreated");
            }
            else
            {
                // View obtained from ElementFactory already has a VirtualizationInfo attached to it
                // which means that the element has been recycled and not created from scratch.
                REPEATER_TRACE_PERF("ElementRecycled");
            }

            if (data != element)
            {
                // Prepare the element
                // If we are phasing, run phase 0 before setting DataContext. If phase 0 is not
                // run before setting DataContext, when setting DataContext all the phases will be
                // run in the OnDataContextChanged handler in code generated by the xaml compiler (code-gen).
                var extension = CachedVisualTreeHelpers.GetDataTemplateComponent(element);
                if (extension != null)
                {
                    // Clear out old data.
                    extension.Recycle();
                    int nextPhase = VirtualizationInfo.PhaseReachedEnd;
                    // Run Phase 0
                    extension.ProcessBindings(data, index, 0 /* currentPhase */, out nextPhase);

                    // Setup phasing information, so that Phaser can pick up any pending phases left.
                    // Update phase on virtInfo. Set data and templateComponent only if x:Phase was used.
                    virtInfo.UpdatePhasingInfo(nextPhase, nextPhase > 0 ? data : null, nextPhase > 0 ? extension : null);
                }
                else if (element is FrameworkElement elementAsFE)
                {
                    // Set data context only if no x:Bind was used. ie. No data template component on the root.
                    // If the passed in data is a UIElement and is different from the element returned by
                    // the template factory then we need to propagate the DataContext.
                    // Otherwise just set the DataContext on the element as the data.
                    var elementDataContext = data;
                    if (data is FrameworkElement dataAsElement)
                    {
                        var dataDataContext = dataAsElement.DataContext;
                        if (dataDataContext != null)
                        {
                            elementDataContext = dataDataContext;
                        }
                    }

                    elementAsFE.DataContext = elementDataContext;
                }
                else
                {
                    MUX_ASSERT(false, "Element returned by factory is not a FrameworkElement!");
                }
            }

            virtInfo.MoveOwnershipToLayoutFromElementFactory(
                index,
                /* uniqueId: */
                m_owner.ItemsSourceView.HasKeyIndexMapping ? m_owner.ItemsSourceView.KeyFromIndex(index) : null);

            // The view generator is the only provider that prepares the element.
            var repeater = m_owner;

#if IS_UNO  //TODO: Uno specific - remove when #4689 is fixed
            repeater.OnUnoBeforeElementPrepared(element, index);
#endif

            // Add the element to the children collection here before raising OnElementPrepared so
            // that handlers can walk up the tree in case they want to find their IndexPath in the
            // nested case.
            var children = repeater.Children;
            if (CachedVisualTreeHelpers.GetParent(element) != repeater)
            {
                children.Add(element);
            }

            repeater.AnimationManager.OnElementPrepared(element);

            repeater.OnElementPrepared(element, index);

            if (data != element)
            {
                m_phaser.PhaseElement(element, virtInfo);
            }

            // Update realized indices
            m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, index);
            m_lastRealizedElementIndexHeldByLayout  = Math.Max(m_lastRealizedElementIndexHeldByLayout, index);

            return(element);
        }
        // Retrieve the element that will be displayed for a specific data item.
        // If the resolved element is not derived from NavigationViewItemBase, wrap in a NavigationViewItem before returning.
        protected override UIElement GetElementCore(ElementFactoryGetArgs args)
        {
            object GetNewContent(IElementFactoryShim itemTemplateWrapper, NavigationViewItemBase settingsItem)
            {
                // Do not template SettingsItem
                if (settingsItem != null && settingsItem == args.Data)
                {
                    return(args.Data);
                }

                if (itemTemplateWrapper != null)
                {
                    return(itemTemplateWrapper.GetElement(args));
                }
                return(args.Data);
            }

            var newContent = GetNewContent(m_itemTemplateWrapper, m_settingsItem);

            // Element is already of expected type, just return it
            if (newContent is NavigationViewItemBase newItem)
            {
                return(newItem);
            }

#if !HAS_UNO_WINUI
            // Accidentally adding a OS XAML NavigationViewItem to WinUI's NavigationView can cause unnecessary confusion for developers
            // due to unexpected rendering, potentially without an easy way to understand what went wrong here. To help out developers,
            // we are explicitly checking for this scenario here and throw a helpful error message so that they can quickly fix their app.
            if (newContent is Windows.UI.Xaml.Controls.NavigationViewItemBase)
            {
                throw new InvalidOperationException("A NavigationView instance contains a Windows.UI.Xaml.Controls.NavigationViewItem. This control requires that its NavigationViewItems be of type Microsoft.UI.Xaml.Controls.NavigationViewItem.");
            }
#endif

            // Get or create a wrapping container for the data
            NavigationViewItem GetNavigationViewItem()
            {
                if (navigationViewItemPool.Count > 0)
                {
                    var nvi = navigationViewItemPool[navigationViewItemPool.Count - 1];
                    navigationViewItemPool.RemoveAt(navigationViewItemPool.Count - 1);
                    return(nvi);
                }
                return(new NavigationViewItem());
            }

            var nvi     = GetNavigationViewItem();
            var nviImpl = nvi;
            nviImpl.CreatedByNavigationViewItemsFactory = true;

            // If a user provided item template exists, just pass the template and data down to the ContentPresenter of the NavigationViewItem
            if (m_itemTemplateWrapper != null)
            {
                if (m_itemTemplateWrapper is ItemTemplateWrapper itemTemplateWrapper)
                {
                    // Recycle newContent
                    var tempArgs = new ElementFactoryRecycleArgs();
                    tempArgs.Element = newContent as UIElement;
                    m_itemTemplateWrapper.RecycleElement(tempArgs);


                    nviImpl.Content                 = args.Data;
                    nviImpl.ContentTemplate         = itemTemplateWrapper.Template;
                    nviImpl.ContentTemplateSelector = itemTemplateWrapper.TemplateSelector;
                    return(nviImpl);
                }
            }

            nviImpl.Content = newContent;
            return(nviImpl);
        }
예제 #7
0
 protected virtual UIElement GetElementCore(ElementFactoryGetArgs args)
 => throw new NotImplementedException();
예제 #8
0
 public UIElement GetElement(ElementFactoryGetArgs args)
 => GetElementCore(args);