항목 컨테이너 생성기입니다.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
'codememo' 카테고리의 다른 글
| Angular CLI를 제거/업그레이드하는 방법은 무엇입니까? (0) | 2023.04.28 |
|---|---|
| Bash를 사용하여 파일 크기를 확인하려면 어떻게 해야 합니까? (0) | 2023.04.28 |
| *ng템플릿에 있는 경우 (0) | 2023.04.28 |
| WPF 창에서 제목 표시줄을 사라지게 하는 방법은 무엇입니까? (0) | 2023.04.28 |
| Azure ARM 템플릿 유효성 검사 오류를 해결하려면 어떻게 해야 합니까? (0) | 2023.04.28 |