codememo

항목 컨테이너 생성기입니다.ContainerFromItem()이 null을 반환합니까?

tipmemo 2023. 4. 28. 20:37
반응형

항목 컨테이너 생성기입니다.ContainerFromItem()이 null을 반환합니까?

제가 좀 이상한 행동을 해서 잘 해결이 안 되는 것 같아요.목록 상자의 항목을 반복할 때ItemsSource 속성. 컨테이너를 가져올 수 없는 것 같습니까?ListBoxItem이 반환될 것으로 예상되지만 null만 수신됩니다.

아이디어 있어요?

제가 사용하는 코드는 다음과 같습니다.

this.lstResults.ItemsSource.ForEach(t =>
    {
        ListBoxItem lbi = this.lstResults.ItemContainerGenerator.ContainerFromItem(t) as ListBoxItem;

        if (lbi != null)
        {
            this.AddToolTip(lbi);
        }
    });

아이템소스가 현재 사전으로 설정되어 있으며 여러 KVP를 포함하고 있습니다.

이 StackOverflow 질문에서 제 사례에 더 잘 맞는 것을 발견했습니다.

데이터 그리드의 행 가져오기

ContainerFromItem 또는 ContainerFromIndex를 호출하기 전에 UpdateLayout 및 ScrollIntoView 호출을 입력하면 데이터 그리드의 해당 부분이 실현되어 ContainerFromItem/ContainerFromItem에 대한 값을 반환할 수 있습니다.

dataGrid.UpdateLayout();
dataGrid.ScrollIntoView(dataGrid.Items[index]);
var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromIndex(index);

DataGrid의 현재 위치를 변경하지 않으려면 이 솔루션이 적합하지 않을 수도 있지만, 문제가 없다면 가상화를 끄지 않고도 작동합니다.

마침내 문제를 해결했습니다...추가함으로써VirtualizingStackPanel.IsVirtualizing="False"내 XAML에 모든 것이 예상대로 작동합니다.

단점으로는 가상화의 모든 성능 이점을 놓쳤기 때문에 로드 라우팅을 비동기로 변경하고 로드하는 동안 목록 상자에 "스피너"를 추가했습니다.

object viewItem = list.ItemContainerGenerator.ContainerFromItem(item);
if (viewItem == null)
{
    list.UpdateLayout();
    viewItem = list.ItemContainerGenerator.ContainerFromItem(item);
    Debug.Assert(viewItem != null, "list.ItemContainerGenerator.ContainerFromItem(item) is null, even after UpdateLayout");
}

디버거를 사용하여 코드를 단계적으로 살펴보고 실제로 검색된 내용이 없는지 확인합니다.as-캐스트가 잘못되었기 때문에 다음으로 돌립니다.null(일반 캐스트를 사용하여 적절한 예외를 얻을 수 있습니다.)

자주 발생하는 한 가지 문제는 다음과 같은 경우입니다.ItemsControl컨테이너가 존재하지 않는 대부분의 항목에 대해 가상화하고 있습니다.

또한 아이템 컨테이너를 직접 다루는 것이 아니라 속성을 바인딩하고 이벤트를 구독하는 것을 권장합니다.ItemsControl.ItemContainerStyle).

이 구독 사용:

TheListBox.ItemContainerGenerator.StatusChanged += (sender, e) =>
{
  TheListBox.Dispatcher.Invoke(() =>
  {
     var TheOne = TheListBox.ItemContainerGenerator.ContainerFromIndex(0);
     if (TheOne != null)
       // Use The One
  });
};

파티에 좀 늦었지만 제 경우에 실패하지 않는 다른 해결책이 있습니다.

추가를 제안하는 많은 솔루션을 시도한 후IsExpanded그리고.IsSelected기본 객체에 연결하고 결합합니다.TreeViewItem스타일, 이것은 어떤 경우에는 대부분 효과가 있지만 여전히 실패합니다...

참고: 제 목표는 오른쪽 창에서 폴더를 클릭하면 다음 창에서 선택되는 미니/사용자 정의 탐색기와 같은 보기를 작성하는 것이었습니다.TreeView익스플로러에서처럼.

private void ListViewItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    var item = sender as ListViewItem;
    var node = item?.Content as DirectoryNode;
    if (node == null) return;

    var nodes = (IEnumerable<DirectoryNode>)TreeView.ItemsSource;
    if (nodes == null) return;

    var queue = new Stack<Node>();
    queue.Push(node);
    var parent = node.Parent;
    while (parent != null)
    {
        queue.Push(parent);
        parent = parent.Parent;
    }

    var generator = TreeView.ItemContainerGenerator;
    while (queue.Count > 0)
    {
        var dequeue = queue.Pop();
        TreeView.UpdateLayout();
        var treeViewItem = (TreeViewItem)generator.ContainerFromItem(dequeue);
        if (queue.Count > 0) treeViewItem.IsExpanded = true;
        else treeViewItem.IsSelected = true;
        generator = treeViewItem.ItemContainerGenerator;
    }
}

여기에 사용되는 여러 가지 트릭:

  • 모든 항목을 위에서 아래로 확장하기 위한 스택
  • 항목을 찾기 위해 현재 레벨의 제너레이터를 사용해야 합니다(정말 중요).
  • 에 대한 반환되지 않는다는 은 최상항발가다시는돌아지오않사는실는다전기목위의▁the사실▁for는.null

지금까지는 아주 잘 작동합니다.

  • 새로운 속성으로 당신의 유형을 오염시킬 필요가 없습니다.
  • 가상화를 사용하지 않도록 설정할 필요가 전혀 없습니다.

를 비활성화하는 것은 효과가 XAML에서 가상화를 .더 합니다.ContainerFromItem

 VirtualizingStackPanel.SetIsVirtualizing(listBox, false);

이렇게 하면 XAML과 코드 사이의 결합을 줄일 수 있으므로 XAML을 터치하여 코드가 깨질 위험을 방지할 수 있습니다.

일 가능성이 .ListBoxItem컨테이너는 현재 보이는 항목에 대해서만 생성됩니다(https://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel(v=vs.110).aspx#Anchor_9) 참조).

사중인경우를 .ListBox▁to다니습으로 전환하는 것을 ListView- 대신 - 상됨에서 ListBox그리고 그것은 지원합니다.ScrollIntoView()가상화를 제어하는 데 활용할 수 있는 방법

targetListView.ScrollIntoView(itemVM);
DoEvents();
ListViewItem itemContainer = targetListView.ItemContainerGenerator.ContainerFromItem(itemVM) as ListViewItem;

에서는 (으)를 하기도 합니다.DoEvents()정적 방법은 여기에서 더 자세히 설명합니다. WPF는 더 많은 코드를 처리하기 전에 바인딩 업데이트가 발생할 때까지 기다리는 방법은 무엇입니까?)

그 밖에도 몇 가지 사소한 차이점이 있습니다.ListBox그리고.ListView컨트롤(What is ListBox와 ListView의 차이) - 사용 사례에 본질적으로 영향을 미치지 않아야 합니다.

스택 패널을 가상화하고 있습니다.IsVirtualizing="False" 컨트롤을 흐리게 합니다. 아래 구현을 참조하십시오.그것은 제가 같은 문제를 피하는 데 도움이 됩니다.애플리케이션 가상화 스택 패널을 설정합니다.가상화="항상 "참"입니다.

자세한 내용은 링크 참조

/// <summary>
/// Recursively search for an item in this subtree.
/// </summary>
/// <param name="container">
/// The parent ItemsControl. This can be a TreeView or a TreeViewItem.
/// </param>
/// <param name="item">
/// The item to search for.
/// </param>
/// <returns>
/// The TreeViewItem that contains the specified item.
/// </returns>
private TreeViewItem GetTreeViewItem(ItemsControl container, object item)
{
    if (container != null)
    {
        if (container.DataContext == item)
        {
            return container as TreeViewItem;
        }

        // Expand the current container
        if (container is TreeViewItem && !((TreeViewItem)container).IsExpanded)
        {
            container.SetValue(TreeViewItem.IsExpandedProperty, true);
        }

        // Try to generate the ItemsPresenter and the ItemsPanel.
        // by calling ApplyTemplate.  Note that in the 
        // virtualizing case even if the item is marked 
        // expanded we still need to do this step in order to 
        // regenerate the visuals because they may have been virtualized away.

        container.ApplyTemplate();
        ItemsPresenter itemsPresenter = 
            (ItemsPresenter)container.Template.FindName("ItemsHost", container);
        if (itemsPresenter != null)
        {
            itemsPresenter.ApplyTemplate();
        }
        else
        {
            // The Tree template has not named the ItemsPresenter, 
            // so walk the descendents and find the child.
            itemsPresenter = FindVisualChild<ItemsPresenter>(container);
            if (itemsPresenter == null)
            {
                container.UpdateLayout();

                itemsPresenter = FindVisualChild<ItemsPresenter>(container);
            }
        }

        Panel itemsHostPanel = (Panel)VisualTreeHelper.GetChild(itemsPresenter, 0);


        // Ensure that the generator for this panel has been created.
        UIElementCollection children = itemsHostPanel.Children; 

        MyVirtualizingStackPanel virtualizingPanel = 
            itemsHostPanel as MyVirtualizingStackPanel;

        for (int i = 0, count = container.Items.Count; i < count; i++)
        {
            TreeViewItem subContainer;
            if (virtualizingPanel != null)
            {
                // Bring the item into view so 
                // that the container will be generated.
                virtualizingPanel.BringIntoView(i);

                subContainer = 
                    (TreeViewItem)container.ItemContainerGenerator.
                    ContainerFromIndex(i);
            }
            else
            {
                subContainer = 
                    (TreeViewItem)container.ItemContainerGenerator.
                    ContainerFromIndex(i);

                // Bring the item into view to maintain the 
                // same behavior as with a virtualizing panel.
                subContainer.BringIntoView();
            }

            if (subContainer != null)
            {
                // Search the next level for the object.
                TreeViewItem resultContainer = GetTreeViewItem(subContainer, item);
                if (resultContainer != null)
                {
                    return resultContainer;
                }
                else
                {
                    // The object is not under this TreeViewItem
                    // so collapse it.
                    subContainer.IsExpanded = false;
                }
            }
        }
    }

    return null;
}

아직 문제가 있는 사람이라면 누구나 첫 번째 선택 변경 이벤트를 무시하고 스레드를 사용하여 기본적으로 통화를 반복함으로써 이 문제를 해결할 수 있었습니다.제가 하게 된 일은 다음과 같습니다.

private int _hackyfix = 0;
    private void OnMediaSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        //HACKYFIX:Hacky workaround for an api issue
        //Microsoft's api for getting item controls for the flipview item fail on the very first media selection change for some reason.  Basically we ignore the
        //first media selection changed event but spawn a thread to redo the ignored selection changed, hopefully allowing time for whatever is going on
        //with the api to get things sorted out so we can call the "ContainerFromItem" function and actually get the control we need I ignore the event twice just in case but I think you can get away with ignoring only the first one.
        if (_hackyfix == 0 || _hackyfix == 1)
        {
            _hackyfix++;
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            OnMediaSelectionChanged(sender, e);
        });
        }
        //END OF HACKY FIX//Actual code you need to run goes here}

EDIT 10/29/2014: 실제로 스레드 디스패처 코드가 필요하지 않습니다.첫 번째 선택 변경 이벤트를 트리거한 다음 이후 이벤트가 예상대로 작동하도록 이벤트를 종료하도록 null로 설정할 수 있습니다.

        private int _hackyfix = 0;
    private void OnMediaSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        //HACKYFIX: Daniel note:  Very hacky workaround for an api issue
        //Microsoft's api for getting item controls for the flipview item fail on the very first media selection change for some reason.  Basically we ignore the
        //first media selection changed event but spawn a thread to redo the ignored selection changed, hopefully allowing time for whatever is going on
        //with the api to get things sorted out so we can call the "ContainerFromItem" function and actually get the control we need
        if (_hackyfix == 0)
        {
            _hackyfix++;
            /*
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            OnMediaSelectionChanged(sender, e);
        });*/
            return;
        }
        //END OF HACKY FIX
        //Your selection_changed code here
        }

언급URL : https://stackoverflow.com/questions/6713365/itemcontainergenerator-containerfromitem-returns-null

반응형