TextBlock stop reacting to data binding after changing Inlines - c#

My goal is having TextBox over TextBlock which upon finished typing changes its Inlines in order to colorize certain keywords.
My control xaml:
<Grid>
<TextBlock Text="{Binding Text, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Foreground="AliceBlue"
FontFamily="Consolas"
Name="MyTextBlock"/>
<TextBox AcceptsTab="True"
AcceptsReturn="True"
TextWrapping="WrapWithOverflow"
Foreground="Transparent"
Background="Transparent"
Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
TextChanged="OnTextChanged"
PreviewKeyDown="OnPreviewKeyDown"
Name="MyTextBox"/>
</Grid>
Part of code that is problematic:
private void HandleFinishedTypingEvent(object sender, EventArgs e)
{
//Timer is being used for end typing detection
(sender as Timer).Stop();
Dispatcher.Invoke(() =>
{
var inlines = MyTextBlock.Inlines;
inlines.Clear();
Run run;
foreach (string word in Text.Split(' '))
{
run = new Run(word + " ");
if (word == "keyword")
{
run.Foreground = Brushes.Lime;
inlines.Add(run);
}
else
{
run.Foreground = Brushes.AliceBlue;
inlines.Add(run);
}
}
});
}
The code itself work but after that TextBlock doesn't update by property change, only when code updating Inlines runs.

Related

selected value cannot be set

I have a combobox which gets its Items from some scan function.
If the user select an element, in the next time, the user's chosen item should be selected (if it is present on the scan function output). The problem is that I cannot select it.
Here is the declaration of the ComboBox:
<ComboBox Grid.Column="1" Grid.Row="0" Margin="5" Name="SerialPortNames" Text="{Binding Name}" IsEditable="False"/>
and here what I have tried so far:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
string portNameSetting = Settings.Default["SerialPortName"].ToString();
SerialPortNames.ItemsSource = SerialPort.GetPortNames();
foreach (string SerialPortNameItem in SerialPortNames.Items)
{
if (SerialPortNameItem == portNameSetting)
{
SerialPortNames.Text = SerialPortNameItem; // why this is not working
break;
}
}
}
by debugging this, I get the item selected in the combobox, but it seems that something override it and it is empty!
In your code you Binded the Text propery and also setting it from code behind
Remove Text="{Binding Name}" from the combobox
<ComboBox Width="200" Height="200" Grid.Column="1" Grid.Row="0" Margin="5" Name="SerialPortNames" IsEditable="False"/>

Reach a TextBlock from a specific ListViewItem from the ListView in Windows Phone 8.1 XAML programmatically

I am a new developer on Windows Phone 8.1, I am try to reach a specific ListView item from the ListView collection and be able to color it or color the TextBock inside of it, But I can't reach the item or reach any of items inside of ListView, Please take a look for my below code :
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
SQLiteRT db1 = new SQLiteRT();
var db_connection = await db1.Connection("MyDB.sqlite");
List<MyTBL> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;");
db_connection.Close();
LV_Options.ItemsSource = t_list;
}
// my List View called LV_Options
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView lv1 = sender as ListView;
if (lv1 == null)
return;
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
tb.FontSize = 17; // here I got debug error (it not worked !!!!!!!)
var item = LV_Options.Items.ElementAt(3); // this seems not work also !!!!
item.BackColor = Color.LightSteelBlue;
}
As you can see above, I tried to reach a specific item by LV_Options.Items.ElementAt(3) but it doesn't work! I also tried to reach the TextBlock from the selected List view item, but also not worked !
(Updated)
XAML code :
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Name="TB_Rslt" Text="Here result of your answer" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
<TextBlock Text="page title" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,10,19,15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="TB_Question" Text="Choose Answer " Margin="0,0,25,0" HorizontalAlignment="Right" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" />
<TextBlock Name="TB_EnWord" Text="" Margin="90,0,15,0" HorizontalAlignment="Left" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" TextAlignment="Right" />
<StackPanel Grid.Row="1" Margin="5,22,0,0">
<ListView Name="LV_Options" SelectionChanged="LV_Options_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding AMean1}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<Button Name="Btn_Answer" Content="Ansewr" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Bottom" Click="Btn_Answer_Click"/>
My application is a quiz application that offer 4 choices/options as answers for each question, and when user select a true answer, I want to highlight the true answer(true choice) by make its background to green, and if the user selected wrong answer/option I want to make the background of that answer (a specific List View item) with red.
Any help please ?
You're not going to be able to access an element inside a data template like that. Instead, leverage the binding to a view model to set the color and other view-related properties. First, create a wrapper view model for your data class:
public class MyTBLViewModel : INotifyPropertyChanged
{
public MyTBL Entity
{
get { return _entity; }
}
private readonly MyTBL _entity;
public Brush Highlight
{
get { return _brush; }
set
{
_brush = value;
RaisePropertyChanged("Highlight");
}
}
private Brush _highlight;
public double ItemFontSize
{
get { return _itemFontSize; }
set
{
_itemFontSize = value;
RaisePropertyChanged("ItemFontSize");
}
}
private Brush _itemFontSize;
public MyTBLViewModel(MyTBL entity)
{
_entity = entity;
_highlight = new SolidColorBrush(Colors.Transparent);
_itemFontSize = 12;
}
public event PropertyChangedEventArgs PropertyChanged;
protected void RaisePropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propName));
}
}
Use this as your ItemsSource:
List<MyTBLViewModel> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;")
.AsEnumerable().Select(entity => new MyTBLViewModel(entity)).ToList();
Now in your view, bind the view elements to "Highlight" and "ItemFontSize", and to any other properties you like:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6" Background="{Binding Highlight}">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding Entity.AMean1}" TextWrapping="Wrap"
FontSize="{Binding ItemFontSize}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Finally, you can get the data item from the SelectionChangedEventArgs -- use it to update your view-related properties:
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Color.LightSteelBlue);
item.ItemFontSize = 17;
}
foreach (var item in e.RemovedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Colors.Transparent);
item.ItemFontSize = 12;
}
}
var item = LV_Options.Items.ElementAt(3);
This line is incorrect. It will not return you a TextBlock. I don't know what a .BackColor is, and it should not compile. The Items property in a ListView will return you a list of ListViewItems. If you want to access the inside element from a ListViewItem, you'll need to access the ContentTemplateRoot property.
Do not use var ever. It lets you assume that you know the type, whereas if you explicitly typed the declaration you would realize you're doing it wrong.
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
What is a MyTBL type? FindName is only available to framework DependencyObjects so I'm assuming it's a user control? You have to provide a lot more code to show us what you're doing and what you're setting the ListView's ItemsSource and ItemTemplate with and what these errors are and how you have 2 breaking debug errors at once and what the error messages are.
Comprehending runtime error messages is a huge part of being a good developer.

Listpicker tap event in fullscreen mode not working correctly

I'm developing windows phone 8 application.
I'm using ListPicker option.
My Listpicker code
<toolkit:ListPicker x:Name="LPfilter" Foreground="White" BorderThickness="0" Margin="300,0,0,0" Height="80" Width="50" Visibility="Visible">
<toolkit:ListPicker.Background>
<ImageBrush ImageSource="/Assets/Images/filters.png"/>
</toolkit:ListPicker.Background>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" Visibility="Collapsed" Foreground="Red"/>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<TextBlock Tap="TextBlock_Tap">
<Run Text="{Binding name}"/>
</TextBlock>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
values bind to listpicker using webservice (json format).
Json result comes like this
[
-{
id: "9",
name: "Pizza",
root_id: "4",
level: "1",
},
-{
id: "10",
name: "Fine Dinind",
root_id: "4",
level: "1",
},
-{
id: "11",
name: "Fast Food",
root_id: "4",
level: "1",
},
....
]
c# code for bind values
public void businesscatbind()
{
string bus_caturl = "http://xxxxx.com/Service/filterquery.php?rootid=" + bus_catval;
WebClient bus_catwc = new WebClient();
bus_catwc.DownloadStringAsync(new Uri(bus_caturl), UriKind.Relative);
bus_catwc.DownloadStringCompleted += bus_catwc_DownloadStringCompleted;
}
void bus_catwc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var bus_catdata = e.Result;
var bus_catvalue = JsonConvert.DeserializeObject<List<bus_catbinddata>>(bus_catdata);
LPfilter.ItemsSource = bus_catvalue;
}
Problem occur in this event
private void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
bus_catbinddata elements = LPfilter.SelectedItem as bus_catbinddata;
int val = LPfilter.SelectedIndex;
filterid = int.Parse(elements.id);
MessageBox.Show(filterid.ToString());
}
My problem
First time i click pizza it's show the value = Id value 9 in msgbox
next i click Fine Dining it's show the value = Id value 9 in msgbox
next i click Fast Food it's show the value =10 in msgbox (10 Is the idvalue of FineDining)
next i click Italian it's show the value = 11 in msgbox (11 is the Idvalue of Fastfood)
previews selected item value is show in alert
Output
How to solve this issue.
Use Listpicker SelectionChanged event instead
<toolkit:ListPicker x:Name="LPfilter" Foreground="White" BorderThickness="0" Margin="300,0,0,0" Height="80" Width="50" Visibility="Visible" SelectionChanged="listPicker_SelectionChanged>
in Xaml page
<toolkit:ListPicker x:Name="listPicker" Foreground="White" BorderThickness="0" Margin="300,0,0,0" Height="80" Width="50" Visibility="Visible" SelectionChanged="listPicker_SelectionChanged" >
<toolkit:ListPicker.Background>
<ImageBrush ImageSource="/Assets/Images/filters.png"/>
</toolkit:ListPicker.Background>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding name}" Visibility="Collapsed" Foreground="Red"/>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<TextBlock Tap="TextBlock_Tap">
<Run Text="{Binding name}"/>
</TextBlock>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
In .cs File
void listPicker_SelectionChanged(object sender, SelectionChangedEventArgs args)
{
if (this.listPicker.SelectedItems != null && this.listPicker.SelectionMode == SelectionMode.Multiple)
{
for (int i = 0; i < this.listPicker.SelectedItems.Count; i++)
{
string str = ((Items)(this.listPicker.SelectedItems[i])).Name;
if (i == 0)
{
MessageBox.Show("Selected Item(s) is " + str);
}
else
{
//Some Code
}
}
}
else if (this.listPicker.SelectionMode == SelectionMode.Single)
{
MessageBox.Show("Selected Item is " + ((Items)this.listPicker.SelectedItem).Name);
}
}
It seems that the "TextBlock_Tap" event fires up before the "ListPicker" changes its selection.
You can try to use the "SelectionChanged" event of the ListPicker - although it seems that you are not using it because you might want to select the same option twice. In that case, you can add a dummy item to the top of your list saying something like "Select Item" and in the "SelectionChanged" event in the end set the selected item of list picker to your dummy item.
EDIT:
In this method -
void bus_catwc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
var bus_catdata = e.Result;
var bus_catvalue = JsonConvert.DeserializeObject<List<bus_catbinddata>>(bus_catdata);
LPfilter.ItemsSource = bus_catvalue;
}
add a new "bus_catbinddata" to the "var bus_catvalue" - it is a list - insert your dummy itme at index 0. then in selection changed event of list picker, add a condition if(ListPicker.SelectedIndex > 0) and only if this condition is true, the code will execute.
I haven't tried this specifically with ListPicker's full mode item template, but you can try to get corresponding model for tapped TextBlock from DataContext :
private void TextBlock_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
TextBlock textBlock = (TextBlock)sender;
bus_catbinddata elements = textBlock.DataContext as bus_catbinddata;
filterid = int.Parse(elements.id);
MessageBox.Show(filterid.ToString());
}

WPF Combbox Wildcard Substring Search

I have the following properties set on my combobox-
<ComboBox ItemsSource="{Binding AllLines, Mode=OneWay}" Grid.Column="1" SelectedItem="{Binding SelectedLine}" Margin="4"
Visibility="{Binding ShowLines, Converter={StaticResource BoolToVisible}}" AlternationCount="2"
IsTextSearchEnabled="True" IsEditable="True" TextSearch.TextPath="SearchText" IsTextSearchCaseSensitive="False"
ItemContainerStyle="{StaticResource alternatingWithTriggers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Margin="2,0,2,0" FontWeight="Bold" Text="{Binding Description}"
Visibility="{Binding Description, Converter={StaticResource NullVisibilityConverter}}"></TextBlock>
<TextBlock Margin="2,2,2,4" Text="{Binding Designator}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Following the suggestion outlined here I added a custom Search property that included the three properties I wanted to search against.
WPF: Changing a ComboBox's ItemTemplate removes the ability to jump down the list as you type. Any way to fix this?
public string SearchText {get { return string.Format("{0} | {1} | {2}", Description, ID, Designator); }}
My question is, can I do a wildcard or substring search on my concatenation of properties?
Found an idea here from which I modeled my solution-
http://jacobmsaylor.com/?p=17
I made some small changes such as listening to the KeyUp instead of the KeyDown and making the filter case-insensitive. I also added a check to make sure that the combo box text (the search text) was not null or empty.
In the code behind-
public SelectRouteSegmentDialog()
{
InitializeComponent();
LineComboBox.Items.Filter += FilterPredicate;
}
private bool FilterPredicate(object obj)
{
Line line = obj as Line;
if (string.IsNullOrEmpty(LineComboBox.Text)) return true;
if (line.SearchText != null)
{
if (line.SearchText.IndexOf(LineComboBox.Text, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
return true;
}
return false;
}
else
{
//if the string is null, return false
return false;
}
}
private void combobox_KeyUp(object sender, KeyEventArgs e)
{
if ((e.Key == Key.Enter) || (e.Key == Key.Tab) || (e.Key == Key.Return))
{
//Formatting options
LineComboBox.Items.Filter = null;
}
else if ((e.Key == Key.Down) || (e.Key == Key.Up))
{
LineComboBox.IsDropDownOpen = true;
}
else
{
LineComboBox.IsDropDownOpen = true;
LineComboBox.Items.Filter += this.FilterPredicate;
}
}
And the xaml I set IsEditable=true, IsTextSearchEnabled=false and TextSearch.TextPath equal to the path I wanted displayed for my selected item (otherwise just the ToString was displayed). I also listend to the key up event-
<ComboBox ItemsSource="{Binding AllLines, Mode=OneWay}" Grid.Column="1" SelectedItem="{Binding SelectedLine}" Margin="4"
Visibility="{Binding ShowLines, Converter={StaticResource BoolToVisible}}" AlternationCount="2"
IsEditable="True" TextSearch.TextPath="SearchText" IsTextSearchEnabled="False"
ItemContainerStyle="{StaticResource alternatingWithTriggers}" x:Name="LineComboBox" KeyUp="combobox_KeyUp">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Margin="2,0,2,0" FontWeight="Bold" Text="{Binding DisplayText}"
Visibility="{Binding Description, Converter={StaticResource NullVisibilityConverter}}"></TextBlock>
<TextBlock Margin="2,2,2,4" Text="{Binding Designator}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
My question is, can I do a wildcard or substring search on my concatenation of properties?
Not using the built-in TextSearch mechanism; it's a prefix match only. You can specify the text directly, or provide the path to the property containing the text (as you have done), but you cannot change the matching behavior.
You will have to implement your own text search mechanism to get the kind of behavior you want. Try exploring the TextSearch source code for implementation ideas.

Enumerate over each item of listbox to compare internal element values

I have a listbox with a data template bound to a list<class> in the program.
<DataTemplate x:Key="pTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Ref}" Padding="5,0,0,0"/>
<StackPanel Name="taggedA" Tag="{Binding A}" Orientation="Horizontal">
<TextBlock Name="selectedA" Text="{B}" />
</StackPanel>
<Image Name="ind" Width="40" Height="40" />
</StackPanel>
</DataTemplate>
On button click, I want to go over all the elments of the listbox and check if the stackPanel taggedA's tag == textblock selectedA's text.
This is to be done for each of the items in the list box and the data template is as above. How can this be done?
Easier to compare the binding source directly:
ListBox l = myListBox;
for (int i = 0; i < l.Items.Count; i++)
{
var boundObject = (MyClass)l.Items[i];
MessageBox.Show("They are equal? " + (boundObject.A == boundObject.B));
}
I would agree with #dbaseman. But if you are set on doing it you could do the following:
private void button_click(object sender, RoutedEvent e)
{
foreach(var item in MyListBox.Items)
{
ListBoxItem lbi = MyListBox.ItemContainerGenerator.ContainerFromItem(item);
StackPanel taggedApanel = (lbi.Content as StackPanel).Children[1];
//Do whatever you need to do here
}
}

Categories