I have a ListBox being populated in code. Each item is a simple text/url combination and when an item is selected, it should redirect to the selected item's URL. That is working as expected, but I'm having an issue with the selection of the item in the ListBox. It seems that no matter which item you selected, the first item in the ListBox is being highlighted each time. You are still taken to the correct item, but it's highlighting the wrong one. Any idea?
Edit: This is a problem in IE8, but works as expected in FF3.
Edit: Adding sample code. HeadlineData is a custom class, just basically to hold data to be displayed.
<Canvas x:Name="HeadlineCanvas">
<ListBox x:Name="HeadlineListBox" Width="260" Height="380" BorderBrush="Gainsboro" BorderThickness="1" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Hidden">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<HyperlinkButton MinHeight="20" MaxHeight="40" Width="240" NavigateUri="{Binding Url}" IsTabStop="False">
<TextBlock TextWrapping="Wrap" Text="{Binding Title}"/>
</HyperlinkButton>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Canvas>
C# Code
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
HeadlineData hd = new HeadlineData();
hd.Title = "Title 1";
hd.HeadlineNewsTitle = "Title 1";
hd.LastModifiedTime = DateTime.Now;
hd.Url = "http://www.google.com";
this.HeadlineListBox.Items.Add(hd);
hd = new HeadlineData();
hd.Title = "Title 2";
hd.HeadlineNewsTitle = "Title 2";
hd.LastModifiedTime = DateTime.Now;
hd.Url = "http://www.google.com";
this.HeadlineListBox.Items.Add(hd);
}
}
The problem is that your listbox isn't getting the selection. When you click on the Hyperlink button it is handling the click event so the item doesn't get selected.
You'll need to remove the hyperlink button and then do your navigation on the ListBox SelectionChanged event instead if you want the selection to change.
Related
I have a button that displays the value from a class that I created. Everything works fine, except for the fact that the button content does not refresh once the value of the binding is changed in the code. If I exit the screen and come back, the value is correct. Staying on the same screen does not refresh the button content.
The button code is shown below.
<Grid x:Name="Task1Grid" Grid.Row="0" Grid.Column="0" Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height=".2*"/>
<RowDefinition Height=".6*"/>
<RowDefinition Height=".2*"/>
</Grid.RowDefinitions>
<Button Grid.Row="1" Style="{StaticResource RoundedButtonStyle}" Tag="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Click="StoplightButton_Click" FontFamily="Global User Interface">
<Button.Content>
<Image Stretch="Uniform" Source="{Binding SelectedRepairOrder.TaskStatusGrid[0], Converter={StaticResource TaskStatusToStopLight}, Mode=OneWay}"/>
</Button.Content>
<Button.Background>
<ImageBrush Stretch="Uniform" ImageSource="{Binding SelectedRepairOrder.TaskStatusGrid[0], Converter={StaticResource TaskStatusToStopLight}, Mode=OneWay}"/>
</Button.Background>
</Button>
<Button x:Name="Task0Time" Tag="0" Style="{StaticResource RoundedButtonStyle}" Visibility="{Binding SelectedRepairOrder.TaskStatusGrid[0].NewTaskstatus, Converter=
{StaticResource TaskStatusToVisibility}}" IsEnabled="{Binding ShowForecastFeatures}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="{Binding SelectedRepairOrder.TaskStatusGrid[0].TmTimecmpltask, Converter={StaticResource TaskCompleteTimeToTime}}" Grid.Row="2" Flyout="{StaticResource Task1Flyout}"/>
<TextBlock Grid.Row="0" Text="{Binding ClientInfo.TasksInfo[0].TaskDescription}" TextAlignment="Center" VerticalAlignment="Bottom" FontSize="28"/>
</Grid>
The flyout code is shown below.
<Border x:Name="StopLightBorder" Background="CornflowerBlue" Grid.Row="1" BorderBrush="White" BorderThickness="2">
<Grid x:Name="StopLightGrid" Margin="5" >
<Grid.Resources>
<converter:TaskStatusToStopLight x:Key="TaskStatusToStopLight"/>
<converter:TaskCompleteTimeToTime x:Key="TaskCompleteTimeToTime"/>
<converter:TaskStatusToVisibility x:Key="TaskStatusToVisibility"/>
<Flyout x:Key="Task1Flyout" >
<ListBox ItemsSource="{Binding ForecastTimes}" Tag="0" SelectionChanged="ForecastTimeChanged"/>
</Flyout>
The code which changes the value for the binding is shown below.
private void ForecastTimeChanged(object sender, SelectionChangedEventArgs e)
{
var timeListBox = (ListBox)sender;
var completeTime = Convert.ToDateTime(e.AddedItems[0].ToString());
var taskNum = Convert.ToInt16(((FrameworkElement)sender).Tag);
var result = checkPreviousTaskTimes(completeTime, taskNum);
switch (result)
{
case ForecastResult.ValidTime:
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].TmTimecmpltask = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].DtDateoverride = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].TmTimeoverride = completeTime.ToString();
globalContext.SelectedRepairOrder.TaskStatusGrid[taskNum].SendOverrideForecastTime = true;
globalContext.SelectedRepairOrder.WasChanged = true;
globalContext.SelectedRepairOrder.RecordGrid = "1";
((Popup)((FlyoutPresenter)((FrameworkElement)sender).Parent).Parent).IsOpen = false;
break;
default:
showForecastError(result, completeTime, taskNum);
break;
}
}
The Visibility and IsEnabled both work just fine. Not sure what else I can do at this point. It seems that changing the bound data does not have an effect until you leave the screen. I chased this issue all the way through and saw the changes to the data as well as everything else I expected. The flyout causes the forecasttimechanged method to activate. When we go to save this data to the database, the data is correct. The flyout shows the selected time when viewing it on the screen, which is what I want. I see that highlighted in the flyout.
If there is a better control to use than the button, I am all ears at this point. Here is the tricky part. This forecast time can be set in the application as well as the app you are seeing code from. The app has time in 15 minute increments, but the other program that can update this control can put in any time it wishes.
I know there is some control or parameter that needs to be set in order to make this happen properly, but for the life of me, I cannot find it. I have tried everything for the past 3 days now and nothing works.
Help me please.
I know there is some control or parameter that needs to be set in order to make this happen properly, but for the life of me, I cannot find it. I have tried everything for the past 3 days now and nothing works.
From your code, I guess the problem is that you have not implemented INotifyPropertyChanged for binding property. And your logic is complex, you could realize your feature with the easy way like the follow example.
<Button Content="{Binding SelectItem,Mode=OneWay}">
<Button.Flyout>
<Flyout Placement="Top">
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectItem,Mode=TwoWay}">
</ListBox>
</Flyout>
</Button.Flyout>
</Button>
Bind the button content with SelectItem, And then the button content will be modified automatically if the ListBox SelectedItem changed.
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public List<string> Items { get; set; } = new List<string>();
private string selectItem = "Nico";
public string SelectItem { get { return selectItem; } set { selectItem = value; OnPropertyChanged(); } }
public MainPageViewModel()
{
Items.Add("Nico");
Items.Add("Song");
Items.Add("Xiao");
}
So I have a customised GridView with a data template that contains a TextBox and is populated by a list of a custom class called Player. I need to be able to retrieve both the instance of Player and the text in the TextBox and save them to a new custom class called Score.
<GridView x:Name="gridScore" ItemsSource="{x:Bind PlayerList}" IsItemClickEnabled="True">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Player">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="txtbxGridScore" TextChanged="txtbxGridScoreChangedEventHandler" />
<Image Source="{x:Bind ProfilePicture}"/>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{x:Bind FullName}" />
<TextBlock Text="{x:Bind Alias}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Button x:Name="buttonSave" Content="Save Scores" Style="{StaticResource BarButtonStyle}" Click="buttonSave_Click"/>
I come from a web-based Java background so this is a little bit new to me but it seems like it should be a fairly simple exercise.
Initially, I tried iterating through the GridView upon a Button Click and grabbing each Player item along with the TextBox Text and saving them to a List<> of Score, however, getting the TextBox value proved troublesome.
I then tried initialising a page scope List<> of Score and simply updating it each time the TextBox value was changed, however, I wasn't able to make this work either.
A solution for either approach will work fine for my purposes. Any input is appreciated!
If I correctly understood you this is one of the way to resolve your problem.
So let's assume that your model class Player have this structure:
public class Player {
public int PlayerID { get; set; }
public string ProfilePicture { get; set; }
public string FullName { get; set; }
public string Alias { get; set; }
public float PlayerScore { get; set; } // To store textbox value
}
So you can resolve this by using two way binding.
XAML part will look something like this:
<GridView x:Name="gridScore"
ItemsSource="{x:Bind PlayerList}"
IsItemClickEnabled="True">
<GridView.ItemTemplate>
<DataTemplate x:DataType="data:Player">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="txtbxGridScore"
Text="{x:Bind PlayerScore, Mode=TwoWay}" />
<Image Source="{x:Bind ProfilePicture}" />
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{x:Bind FullName}" />
<TextBlock Text="{x:Bind Alias}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
<Button x:Name="buttonSave"
Content="Save Scores"
Click="buttonSave_Click" />
I have initialized your PlayerList with some dummy data like this:
PlayerList = new ObservableCollection<Player>() {
new Player() {
FullName = "Player A", Alias = "AAA"
},
new Player () {
FullName = "Player B", Alias = "BBB"
}
};
As you can see in XAML I am binding your text box with PlayerScore property of Player model.
When I run this App I get screen like this:
I will input some data into TextBox and click Save button:
When I click on Save it will trigger the event that you wrote in Button part
In that event I have one foreach loop that will iterate through the list and one breakpoint and as you can see on first item "Player A" the PlayerScore value is 10:
Now you can find your players with some ID property or with some other way that you want. This is the most simple way to accomplish what you want.
Remark: This could be solved in a better way using MVVM pattern and other stuff but as you mentioned you are beginner so maybe it is better for you to solve it like this and after that go with more advanced technique. Hope that this was helpful for you.
I am working on a Windows Phone 8.1 app in XAML and C#.
I have a listview, whose item source is set to a CollectionViewSource called MusicSource. On the backend in C#, I have an ObservableCollection called source and the following code populates it by getting getting all the music files on the phone, groups it by artist and then puts them in the CollectionViewSource, which shows them in the listview:
var folders = await folder.GetFoldersAsync();
if (folders != null)
foreach (var fol in folders)
await getMusic(fol);
var files = await folder.GetFilesAsync();
foreach (var file in files)
{
MusicProperties musicProperties = await file.Properties.GetMusicPropertiesAsync();
this.source.Add(new Music((musicProperties.Artist.Length > 0) ? musicProperties.Artist : "Custom", (musicProperties.Title.Length > 0) ? musicProperties.Title : file.Name, (musicProperties.Album.Length > 0) ? musicProperties.Album : "Custom Album", file.Path));
}
itemSource = AlphaKeyGroup<Music>.CreateGroups(source, CultureInfo.CurrentUICulture, s => s.Artist, true);
this.MusicSource.Source = itemSource;
The following is the XAML side of it:
<Page.Resources>
<DataTemplate x:Key="GroupTemplate">
<Grid Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="1">
<TextBlock x:Name="SongTitle" Text="{Binding Title}"
Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock x:Name="ArtistName" Text="{Binding Album}"
Style="{ThemeResource ListViewItemContentTextBlockStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
<CollectionViewSource x:Name="MusicSource" IsSourceGrouped="true" />
<DataTemplate x:Key="headerTemplate">
<StackPanel HorizontalAlignment="Stretch" Width="{Binding ActualWidth, ElementName=contentList}">
<TextBlock Text="{Binding Key}" />
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid>
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<ListView
x:Name="contentList"
SelectionMode="Multiple"
ItemsSource="{Binding Source={StaticResource MusicSource}}"
ItemTemplate="{StaticResource GroupTemplate}">
<ListView.GroupStyle>
<GroupStyle HidesIfEmpty="True" HeaderTemplate="{StaticResource headerTemplate}"/>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
</SemanticZoom>
<Border
x:Name="SearchBorder"
Background="White">
<TextBox
x:Name="Search" TextChanged="TextBox_TextChanged" />
</Border>
</Grid>
So I get something like the following in the listview:
Michael Jackson
Bad
Dangerous
Thriller
Eminem
Not Afraid
The Monster
I have a textbox with the name search that is intended to search the listview.
What should happen is that as I type in the textbox, the listview scrolls to the nearest group whose group header matches the text in the textbox. So if I type "Em", the listview should immediately scroll downwards to the "Eminem" category of items.
How would I achieve this?
Also, is it possible to do the same thing, except scroll to the item whose songTitle attribute matches that of the text in the textbox?
Wow that is one hard problem. I've done something very similar in the past. Basically you want an AJAX solution to an ObservableCollection set. You can achieve this by writing a search function through your AlphaKeyGroup with the help from a Regular Expression match. This should return your element that it needs to scroll to.
As for getting to it to search each text change you need to attach a TextChanged Event to the textbox like so:
<TextBox x:Name="my_search" TextChanged="my_search_TextChanged" Grid.Row="0" />
When it finds a match, you want to scroll to that element with
contentList.ScrollIntoView(matching_element);
Okay, posted the Windows Phone 8.1 Solution : ListView Searching Project
Highlights included the Searching Routine that are added to the GroupKey class.
public T FindMatch(string pattern, GetKeyDelegate getKey)
{
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
foreach (T item in this.Items)
{
string key = getKey(item);
Match match = rgx.Match(key);
if (match.Success)
return item;
}
return default(T);
}
Set ICollectionView.Filter callback to filter your listview everytime the TextChanged event is raised like so:
private void TextBox_TextChanged(object sender, TextChangedEven)
{
ICollectionView view = lvTest.ItemsSource as ICollectionView;
string txtToSearch = searchTextBox.Text;
view.Filter = (p) => { return ((Music)p).ArtistName.Contains(txtToSearch); };
view.Refresh();
}
That way you avoid having to decide where to set the current item when it matches more than one Artist name.
Can someone see what I need to change here? I am displaying an observablecollection of AddressTypeClass items. The object items show up in the listbox instead of the data. I can see the data in the objects in debug mode.
THE XAML.CS FILE:
DataContext MyTableDataContext = new MyTableDataContext();
ObservableCollection<AddressTypeClass> theOC = new ObservableCollection<AddressTypeClass>(new MyTableDataContext().AddressTypes.AsEnumerable()
.Select(lt => new AddressTypeClass
{
AddressTypeID = lt.AddressTypeID,
AddressType = lt.AddressType,
})
.ToList());
this.listBox1.ItemsSource = theOC;
THE XAML FILE:
<ListBox Name="listBox1" Margin="8" Height ="200" Width ="150" FontSize="12" Foreground="#FF2F3806" ItemsSource="{Binding AddressType}" IsSynchronizedWithCurrentItem="True" >
</ListBox>
You need to add an ItemTemplate to your ListBox, e.g.
<ListBox Name="listBox1" Margin="8" Height ="200" Width ="150" FontSize="12" Foreground="#FF2F3806" ItemsSource="{Binding AddressType}" IsSynchronizedWithCurrentItem="True" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=AddressType}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can impove your code by using my ObservableComputations library. In your code you manualy update theOC every time MyTableDataContext.AddressTypes dbSet (I assume you are using EntityFramework) changes (new item or remove) or properties (AddressType.AddressTypeID, AddressType.AddressType) changes. Using AddressType you can automate that process:
DataContext MyTableDataContext = new MyTableDataContext();
ObservableCollection<AddressTypeClass> theOC = MyTableDataContext.AddressTypes.Local
.Selecting(lt => new AddressTypeClass
{
AddressTypeID = lt.AddressTypeID,
AddressType = lt.AddressType,
});
this.listBox1.ItemsSource = theOC;
theOC is ObservableCollection and reflects all the changes in the MyTableDataContext.AddressTypes.Local collection and properties mentioned in the code above. Ensure that all properties mentioned in the code above notify of changes through the INotifyProperytChanged interface.
I've got a ListBox in a WP7 App where I want to do something with an item when the user hold it. The event work's great. My hold method gets called, but I can't detect which element in the list was hold.
ListBox.SelectedItem is always -1 and a code from another post on stackoverflow doens't work:
FrameWorkelement element = (FrameworkElement) e.OriginalSource;
ItemViewModel item = (ItemViewModel) element.DataContext;
I get an InvalidCastException when running it in the second line.
The following code should work.
private void StackPanel_Hold(object sender, GestureEventArgs e)
{
ItemViewModel itemViewModel = (sender as StackPanel).DataContext as ItemViewModel;
string t = itemViewModel.LineOne;
}
Note: before using the DataContext of the sender object, make sure you cast the sender object to the correct class. In this example I use a StackPanel in my DataTemplate:
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" ItemsSource="{Binding Items}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Height="78" Hold="StackPanel_Hold">
<TextBlock Text="{Binding LineOne}" />
<TextBlock Text="{Binding LineTwo}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>