C# WPF Virtualizing ListView Issue - c#

I have the following WPF and C# excerpt in a Visual Studio 2010 application. This code is being caught in the exception when I run my application and saying the content is "{DisconnectedItem}". To me that would mean that my 'loadDesc' function is being called for objects (Checkboxes) that are not currently inside view of the ListView. Why is this? Any help is appreciated.
C# and WPF Code Below:
private void loadDesc(object sender, RoutedEventArgs e)
{
try
{
string num = (string)(((sender as TextBlock).Parent as StackPanel).Children[0] as CheckBox).Content;
if (num == null)
{
return;
}
if (num.Equals("Tamper"))
{
(sender as TextBlock).Text = "";
}
else
{
int x = Convert.ToInt32(num);
dc.ext247[x - 1].desc = ZoneDescriptionsVM.getInstance().zoneDesc[x - 1].description.Trim();
dc.onOff247[x - 1].desc = ZoneDescriptionsVM.getInstance().zoneDesc[x - 1].description.Trim();
}
}
catch (Exception ex)
{
MessageBox.Show((((sender as TextBlock).Parent as StackPanel).Children[0] as CheckBox).Content.ToString());
}
}
<ListView KeyboardNavigation.TabNavigation="Continue" VirtualizingStackPanel.VirtualizationMode="Recycling" BorderThickness="0" Width="260" MaxHeight="400" Margin="10" Name="zonesOnOffStack" SelectionMode="Single">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Visibility="{Binding visible}" IsChecked="{Binding enabled}" Content="{Binding name}" />
<TextBlock Padding="5,-1,0,0" Loaded="loadDesc" Text="{Binding desc, StringFormat={}({0})}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>

I see this is old, so maybe not that helpful, but I wanted to note a few things.
In VS2017 I setup something similar and was unable to reproduce, but I assume this is verbatim code you pasted
Mainly, DisconnectedItem seems to be the key, and that is a binding expression issue. I'm curious if the version of xaml you were using didn't like your stringformat not being in single quotes.
Proof that disconnected item is a binding thing:
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Data/BindingExpressionBase.cs,71f3b982041c21ce
Hope that helps :)

Related

How to focus into AutoCompleteBox

How to focus into AutoCompleteBox? I've tried many of code by searching on stackover but not getting focus. here is some code which i wrote.
<controls:AutoCompleteBox Name="SearchTextBox" IsTextCompletionEnabled="True" SelectedItem="{Binding Code, Mode=TwoWay}" Grid.Column="1" PreviewKeyDown="SearchTextBox_PreviewKeyDown" >
<controls:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Code}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</controls:AutoCompleteBox.ItemTemplate>
</controls:AutoCompleteBox>
and for focus i've creating a class named FocusableAutoCompleteBox
public class FocusableAutoCompleteBox : AutoCompleteBox
{
public new void Focus()
{
var textbox = Template.FindName("SearchTextBox", this) as AutoCompleteBox;
if (textbox != null) textbox.Focus();
}
}
and focus by
new FocusableAutoCompleteBox().Focus();
but errors appears
Object Reference not set to an instance of object
I also tried SearchTextBox.Focus(); but not getting result.
this works for me.
Keyboard.Focus(SearchTextBox);
SearchTextBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));

Unknown space while highlighting part of text in a texblock (WPF)

From trial and error I managed to highlight part of text in a textblock which is in a datatemplate of a listbox bounded to a property of a custom class. But the problem now is that when highlighting the text i get a weird unknown space between the highlighted text and the rest of the text.
Here is part of the XAML
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Name="textBox1" TextChanged="textBox1_TextChanged"/>
<ListBox Grid.Row="1" Name="listBox1">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Name="gridOfListbox" Height="25" Margin="0,2">
<DockPanel Name="dockpanelWithTxtBlock">
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Background="Yellow" Text=""></Run>
<Run Text="{Binding ProductID}"></Run>
</TextBlock>
</DockPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
And here part of the code used
ObservableCollection<TItem> items = new ObservableCollection<TItem>();
TItem[] source = new TItem[] { new TItem("Hello"), new TItem("World"), new TItem("System"), new TItem("SystemDefault"), new TItem("SystemFolder") };
And the method for event changedtext
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
string match = textBox1.Text;
foreach (TItem TItem in listBox1.Items)
{
ListBoxItem lbi = (ListBoxItem)this.listBox1.ItemContainerGenerator.ContainerFromItem(TItem);
TextBlock txtBlck = FindFirstElementInVisualTree<TextBlock>(lbi);
Run bold = (Run)txtBlck.Inlines.FirstInline;
Run normal = (Run)txtBlck.Inlines.LastInline;
string s = bold.Text + normal.Text;
if (s.ToLower().StartsWith(match.ToLower()))
{
bold.Text = s.Substring(0, match.Length);
normal.Text = s.Substring(match.Length);
}
else
{
bold.Text = "";
normal.Text = s;
}
}
}
FindFirstElementInVisualTree method is used to find the textboxes needed to search of.
If anymore code is needed let me know.
I also added an image to demonstrate what the problem is.
An help will be appreciated!
Link for image: http://i.stack.imgur.com/rOj0m.png
When you use Run within a TextBlock in XAML, everything not wrapped in <> are considered actual strings. Having a line break would mean a space. Put the two Runs within the same line (without a space in between too).
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Background="Yellow" Text="" /><Run Text="{Binding ProductID}" />
</TextBlock>
Edit
By the way, I just saw your first question which was marked as duplicate. This question is asked correctly; so you should ask questions in this manner in the future.

ListBox.ItemContainerGenerator.ContainerFromIndex Returns null

For my Windows Phone 8 App I have a Listbox element like below;
when I press multiselect icon on AppBar, I want to show checkboxes inside DataTemplate.
So users can make multiselect on items.
I have 50 elements bound on this Listbox and always at the index 11 ItemContainerGenerator.ContainerFromIndex returns null, plus some other items at rest of the list. So around 10 items out of 50 as returning as null.
There are some answers for WPF like applying Dispatcher.BeginInvoke or UpdateLayout, ScrollIntoView but none of them is working.
On the other hand if I scroll through list and then press AppBar icon it just works fine. But users can directly press on icon right after data bound and they will not see some of the checkboxes.
Is there any workaround for this issue for Windows Phone 8?
<ListBox Name="ResultListBox" ItemsSource="{Binding}"
SelectionChanged="ResultListBox_OnSelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<CheckBox Name="CheckBox" Visibility="Collapsed">
</CheckBox>
<Image Source="{Binding url}"
Width="125"
Height="125"
VerticalAlignment="Top"
Margin="0,0,5,0"></Image>
</StackPanel>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding title}"
VerticalAlignment="Top"
FontFamily="Portable User Interface"></TextBlock>
</StackPanel>
<StackPanel>
<TextBlock Text="{Binding description}"
FontFamily="Portable User Interface"></TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
void appBarButtonSelect_Click(object sender, EventArgs e)
{
//Dispatcher.BeginInvoke(delegate
//{
//});
for (int i = 0; i < ResultListBox.Items.Count; i++)
{
//ResultListBox.UpdateLayout();
//ResultListBox.ScrollIntoView(i);
DependencyObject item = ResultListBox.ItemContainerGenerator.ContainerFromIndex(i);
if (item != null)
{
CheckBox checkBox = FindFirstElementInVisualTree<CheckBox>(item);
if (checkBox != null)
{
checkBox.Visibility = Visibility.Visible;
}
}
else
{
Debugger.Break();
}
}
}
I think you're using ScrollIntoView + UpdateLayout incorrectly,
You're passing it an index, when it needs an object that is directly related to the ItemsSource
So if your ItemsSource is an ObservableCollection do this:
object o = ((ObservableCollection<sample_model>)this.myListBox.ItemsSource)[INDEX];
this.myListBox.ScrollIntoView(o); // call this first
this.myListBox.UpdateLayout(); // call this second
Then your ItemContainerGenerator.ContainerFromIndex(INDEX) will not be NULL.

How to bind multiple eventhandlers on WindowsPhone 8 using XAML?

I'm about to make a control page that has several textblock.
each textblock will be "linked" to another page,
here's my xaml part
.........<phone:LongListSelector x:Name="settingSelector" ItemsSource="{Binding}">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True" Tap="{Binding TapMethod, Mode=TwoWay}" >
<TextBlock Text="{Binding SetTitle}" FontSize="43" />
<TextBlock Text="{Binding SetDescription}" FontSize="19" Margin="0 0 0 10" />
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>..........
when I try this code behind :
.........public class SettingProperty
{
public string SetTitle{get; set;}
public string SetDescription{get; set;}
public string TapMethod{get; set;}
public SettingProperty(string setTitle, string setDescription, string tapMethod)
{
SetTitle = setTitle;
SetDescription = setDescription;
TapMethod = tapMethod;
}
}
List<SettingProperty> DataSetting()
{
List<SettingProperty> settingCollection = new List<SettingProperty>();
settingCollection.Add(new SettingProperty("Saldo", "cek saldo", "saldo_Tap"));
return settingCollection;
}
private void saldo_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
NavigationService.Navigate(new Uri("/saldo.xaml", UriKind.Relative));
}..........
I directly deployed them to my L520, I'm prety sure the culprit is the "Tap" binding on my stackpanel, when I omit it, the code works.
Did I miss something, or my whole method is just wrong?
our understanding of the "Tap" event is wrong.
You have two options here:
Either you clearly specify a METHOD NAME (not a string) as a event handler for the Tap event. For example:
Tap="MyControl_Tap"
In this case you must have a MyControl_Tap method in your control's code behind
OR, since you seem to be using the MVVM pattern, you have to create a ICommand, and then include your whole StackPanel in a Button, like this:
...<DataTemplate>
<Button Command="{Binding MyCommandProperty}">
<StackPanel toolkit:TiltEffect.IsTiltEnabled="True" >
<TextBlock Text="{Binding SetTitle}" FontSize="43" />
<TextBlock Text="{Binding SetDescription}" FontSize="19" Margin="0 0 0 10" />
</StackPanel>
</Button>
...

Databinding with a listbox

I have a bit of code that reads a json response from an HTTP server, it then parses this and inserts the data into a ListBox control.
The event I fire off when the download is complete is the following:
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
DataContractJsonSerializer ser = null;
try
{
ser =
new DataContractJsonSerializer(typeof(ObservableCollection<UserLeaderboards>));
ObservableCollection<UserLeaderboards> users =
ser.ReadObject(e.Result) as ObservableCollection<UserLeaderboards>;
foreach (UserLeaderboards em in users)
{
int Fid = em.id;
string Fusername = em.username;
int Fscore = em.score;
lstbLeaders.Items.Add(Fid + Fusername + Fscore);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Now, when I do the items.add I presume it's just joining up the 3 variables and adding it to one column in the ListBox. This works fine and I see all 3 items joined up and displayed.
I want to separate this and make it look a bit nicer so I've created some XAML to try and bind the variables to textblocks. The following is just binding the username. I also have a public class that get/sets all 3 variables.
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0"
Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<DataTemplate>
<TextBlock Text="{Binding Source=Fusername}" />
</DataTemplate>
</ListBox>
When running the above I get nothing displayed at all. I have a feeling it's something simple?
Thanks.
To display a simple string your xaml should look like this:
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0"
Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
and you have to provide an object instead of a simple string if you want to split the properties to make it look nicer. If you just add Fid + Fusername + Fscore you will end up with a plain string.
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0"
Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" />
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Score}" />
</StackPanel>
</DataTemplate>
<ListBox.ItemTemplate>
</ListBox>
You will need a view class:
public class UserView
{
public string Id {get;set;}
public string Name {get;set;}
public int Score {get;set;}
}
in your code behind:
var usersList = new List<UserView>();
foreach (UserLeaderboards em in users)
{
int Fid = em.id;
string Fusername = em.username;
int Fscore = em.score;
usersList.Add(new UserView { Id = Fid, Name = Fusername, Score = Fscore} );
}
lstbLeaders.ItemsSource = usersList;
Further notes:
Why not bind the ObservableCollection<UserLeaderboards> direcectly to the list box?
If there is no reason to convert to an other type skip the foreach part of the code and simply set lstbLeaders.ItemsSource = users;.
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
try
{
var ser = new DataContractJsonSerializer(
typeof(ObservableCollection<UserLeaderboards>));
var users = ser.ReadObject(e.Result)
as ObservableCollection<UserLeaderboards>;
lstbLeaders.ItemsSource = users;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Take look at the MVVM pattern. If you want to work with XAML you should know about this. It simplifies your work and creates cleaner code.
If you want to add edit functionality or data can change you may need to implement INotifyPropertyChanged on the View class.
You can use type inference which especially helps when working with cumbersome class names. var list = new ObservableCollection<SomeLongTypeName>() saves much typing and screen estate.
Hungarian notation makes me cringe ;)
I think you missed the ItemTemplate.
Try this
<ListBox Height="346" HorizontalAlignment="Left" Margin="5,221,0,0" Name="lstbLeaders" VerticalAlignment="Top" Width="446">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Source=Fusername}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Categories

Resources