I'm probably not even asking this correctly, I am new to c#, but trying to help my 14 year-old son learn. I've created a listbox with items created with an ObservableCollection. Here is the XAML:
<ListBox x:Name="listBox1" ItemsSource="{Binding}" Margin="105,205,886,63"
IsTabStop="True" SelectionChanged="PrintText"
ScrollViewer.VerticalScrollBarVisibility="Hidden" TabIndex="5" FontSize="36"
Background="Transparent" Foreground="#FF55B64C" FontFamily="Arabic Typesetting"
FontWeight="Bold" IsDoubleTapEnabled="False" SelectionMode="Single" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="blockNameList" Text="{Binding name}"/>
<TextBlock Text=" #"/>
<TextBlock Name="blockIdList" Text="{Binding id}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Here is how I created the ListBox Items:
var client = new HttpClient();
var uri = new Uri("http://theurlImusing");
Stream respStream2 = await client.GetStreamAsync(uri);
// DataContractJsonSerializer ser2 = new DataContractJsonSerializer(typeof(RootObject));
// RootObject feed2 = (RootObject)ser2.ReadObject(respStream2);
DataContractJsonSerializer ser = null;
ser = new DataContractJsonSerializer(typeof(ObservableCollection<RootObject>));
ObservableCollection<RootObject> feed2 = ser.ReadObject(respStream2) as ObservableCollection<RootObject>;
var cardList = new List<RootObject>();
foreach (RootObject returnfeed in feed2)
{
string cid = returnfeed.id;
string cardname = returnfeed.name;
listBox1.Items.Add(new RootObject { id=cid, name=cardname });
}
I thought I would just use the SelectionChanged="PrintText" property of the listbox so that when I clicked on a listbox item, it would just change a textblock's text value. Ultimately, that is all I am trying to do...set a textblock or textbox to be equal to the "id" value that is clicked on in the ListBox.
void PrintText(object sender, SelectionChangedEventArgs args)
{
//What do I put in here??
}
Thanks very much for any insight! I need it!!
This is something that is much easier to do using data binding. You can bind the TextBlock.Text property directly to the ListBox using an ElementName binding:
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedItem.id}" />
Alternatively, if you set set SelectedValuePath="id" on the ListBox, then binding to SelectedValue will give you the "id" property:
<ListBox x:Name="listBox1" SelectedValuePath="id" ... />
<TextBox Text="{Binding ElementName=listBox1,Path=SelectedValue}" />
As a side note (as #Rachel already noted in comments): you may as well just set the ItemsSource, rather than looping through and adding each manually. All you need is this:
listBox1.ItemsSource = feed2;
Edit
Ok, if you wanted to use the procedural approach, here's how you would do it. (No one would recommend this approach, especially if you're learning/teaching. Try to make full use of data binding, and view-viewmodel separation.)
void PrintText(object sender, SelectionChangedEventArgs args)
{
var listBox = (ListBox)sender;
RootObject selectedItem = listBox.SelectedItem;
someTextBox.Text = selectedItem.id;
}
If all you want to do is click an item in the ListBox and get it to show up in the TextBox, you don't need fancy binding (in that other answer) to do it. You can simply add a MouseUp event in the ListBox XAML:
MouseUp="ListBox1_MouseUp"
This would work similar to the SelectionChanged event you wanted to use.
You then right-click that function name in the XAML page and select "Go to definition". It will create the next function for you:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
}
Simply add in there to update the TextBox you want with the SelectedItem values from sender:
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
ListBox lstBox = (ListBox)sender;
ListBoxItem item = lstBox.SelectedItem;
if (item != null) // avoids exception when an empty line is clicked
{
someBox.Text = item.name;
someOtherBox.Text = item.id;
}
}
I later found that blockNameList and blockIdList are not accessible via intellisense because they are within the DataTemplate of the ListBox, so I put someBox and someOtherBox, as references to other TextBoxes you would have to add to the XAML, outside of the ListBox. You would not re-write data inside the ListBox on the same item by clicking it. Even if you could reach the template's TextBlock to do it, you'd just be re-writing that same item with its own values, since it would be the SelectedItem!
Even though there are those that don't recommend this approach because they like binding everything - and in some cases you want binding to occur so that controls on the page update as a result of dependencies (i.e. do one thing to cause another), I find that manual methods of clicking a button/item/control to update something are just fine and avoid all the model/MVVM BS that has taken over WPF and over-complicated it.
Related
I'm having some trouble to set one property (MouseDown event) value based on it's sender. I have "MyPhotoA" and "MyPhotoB" binded to an observableCollection. Both trigger the same event "MyOnClick" Here is the xaml:
... stuff
<DataTemplate>
<Image Source="{Binding MyPhotoA, UpdateSourceTrigger=LostFocus}" MouseDown="MyOnClick" />
</DataTemplate>
... stuff
<DataTemplate>
<Image Source="{Binding MyPhotoB, UpdateSourceTrigger=LostFocus}" MouseDown="MyOnClick" />
</DataTemplate>
... stuff
These two datatemplates are used for two datagridtemplatecolumns in the datagrid. Hence there are two columns of images and the user clicks one. I want to set the source on the image clicked.
The event "MyOnClick" is something like this:
private void MyOnClick(object sender, MouseButtonEventArgs e)
{
var myImage File.ReadAllBytes("c:\\MyImage.jpeg")
var dc = (sender as System.Windows.Controls.Image).DataContext;
MyModelClass itemSelected = (MyModelClass)dc;
itemSelected.PhotoA = myImage;//Setting PhotoA
itemSelected.PhotoB = myImage;//Setting PhotoB
//How to set the photo based on "sender" property? Like:
//sender.[somestuff]=myImage;
}
I'd like to use the same method to set data in PhotoA and PhotoB based on the sender property binded to it. So if user click in the "PhotoA" DataGrid cell, the image is setted to "PhotoA". If click is done in "PhotoB" then "PhotoB" data is setted.
!!!Note!!!: I don't want tricks like
If (sender.name="PhotoA") then
itemSelected.PhotoA = myImage;
else
itemSelected.PhotoB = myImage;
Thanks in advance
[Workaround Update]
I could not find the answer so I used a workaround:
1)edit xaml code, adding a property "name" to each Photo:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Name="ImageMyPhotoA" Source="{Binding Photo}" MouseDown="MyOnClick" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
On the event, I manually added the bin to the the observable collection.
private void MyOnClick(object sender, MouseButtonEventArgs e)
{
var myImage = File.ReadAllBytes("c:\\MyImage.jpeg");
var dc = (sender as Image).DataContext;
MyModelClass itemSelected = (MyModelClass)dc;
var senderName = (sender as Image).Name;
if (senderName == "ImagePhotoA")
{
itemSelected.PhotoA = myImage;
}
if (senderName == "ImagePhotoB")
{
itemSelected.PhotoB = myImage;
}
}
Conclusion
Setting properties in "MouseDown" event based on Sender (Sender.[SomeSenderProperty] = "Something") seems not possible OR over complicated. I suggest to mark the sender's name in xaml (like the example). Thanks for the good fellows for your help, I really appreciate.
You're essentially trying to set the source property of an image the user clicked.
When you do that you want it to persist, presumably, and you probably won't want to overwrite the binding so make your binding twoway.
<Image Source="{Binding MyPhotoA, Mode=TwoWay}"
In your click handler.
Cast your sender to image.
var img = sender as Image;
(You should routinely null check when you do as anything.)
But this gives you a reference to the appropriate image control to work with.
Set the value.
As Clemens points out, I was overcomplicating this with:
img.SetCurrentValue(SourceProperty, Abitmapimage);
And you can just do:
img.Source = new BitmapImage(new Uri(#"C:\MyImage.jpeg"));
I implemented a collection of Hyperlink elements using WPF:
var controlLinks = new List<Hyperlink>();
if (issueLinks != null)
{
foreach (var link in issueLinks)
{
var tempLink = new Hyperlink()
{
NavigateUri = new Uri(link.link)
};
controlLinks.Add(tempLink);
}
}
ListIssueLinks.ItemsSource = controlLinks;
Collections is successfuly filled, now I link ListIssueLinks view to this collection.
<ListView Name="ListIssueLinks" Height="100" >
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
Here I've got a problem, the issue is I'm new to WPF and have no idea how properly implement formatting (for example, to present NavigateUri or Name only on UI) and implement generic handler for click on any element. Something like this:
private void Hyperlink_OnClick(object sender, RoutedEventArgs e)
{
var clickedLink = (Hyperlink) sender;
System.Diagnostics.Process.Start(clickedLink.NavigateUri.ToString());
}
I tried DataTemplate, tried a lot of other variants, googled really a lot, but still have no clue how to do that. Could you please suggest any easy and elegant solution?
DataTemplate is your best bet, here's how you could implement it in your case.
<ListView Name="ListIssueLinks" Height="100">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink NavigateUri="{Binding}" RequestNavigate="Link_RequestNavigate">
<TextBlock Text="{Binding}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Then in your code behind, I would simply bind directly to your issueLinks (no need to build up HyperLinks in code).
List<string> issueLinks = new List<string>()
{
"http://www.google.com",
"http://www.stackoverflow.com",
};
ListIssueLinks.ItemsSource = issueLinks;
Then your RequestNavigate event handler needs to start your process
private void Link_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
I didnt fully understand what is your problem but i have two things that you should know that exist.
ObservableCollection
i think you should consider use it instead of a simple List because it has advantages if you need to bind the List to The View with your ListView for example. for more info read here: ObservableCollection Class
XAML side
the second thing i think you tried to explain is how to take properties and show them in your list view. this means you need to write it on the XAML side and explore about it farther, you can bind the properties after that with inotifypropertychanged if the data in the list is going to change with any reason.
hope i helped.
In my windows phone app I've implemented data binding which is not yielding me expected results.
My functionality is I've a list box in which I've two textboxes which are data bound.
When I click the textbox datepicker/timepicker will open and the selected value should reflect in the textbox.
The xaml code for the listbox data template is as follows
<TextBox Visibility="{Binding TBVisibility}" IsReadOnly="{Binding TBReadOnly}" InputScope="{Binding Numeric}" AcceptsReturn="{Binding MultiLine}" Width="{Binding TBWidth}" TextWrapping="Wrap" Text="{Binding Path=TBText, Mode=TwoWay}" GotFocus="TextBox_GotFocus_1" KeyUp="TextBox_KeyUp_1" LostFocus="TextBox_LostFocus_1" />
<TextBox Visibility="{Binding TB2Visibility}" IsReadOnly="True" Width="140" Text="{Binding TB2Text, Mode=TwoWay}" GotFocus="TextBox_GotFocus_2" />
I'm launching the datepicker and timepicker as follows
private void LaunchDatePicker(TFDetails field)
{
datePicker = new CustomDatePicker
{
IsTabStop = false,
MaxHeight = 0,
Value = field.SelectedDate
};
datePicker.DataContext = field;
datePicker.ValueChanged += DatePicker_ValueChanged;
LayoutRoot.Children.Add(datePicker);
datePicker.ClickDateTemplateButton();
}
Where as "field" is the datacontext of the listbox.
The ValueChanged events are as follows
private void DatePicker_ValueChanged(object sender, DateTimeValueChangedEventArgs e)
{
DatePicker currentDP = sender as DatePicker;
TFDetails callingField = currentDP.DataContext as TFDetails;
if (callingField != null)
{
callingField.SelectedDate = currentDP.Value;
callingField.TBText = currentDP.ValueString;
}
}
When I change the time its not reflecting in the textbox. I wrote INotifyChangedProperty also.
May I know what mistake I could possible be doing here.
I actually have the same code in a similar UI page where it works perfectly. i don't know what mistake I'm doing here.
Thanks.
ListBox is a collection control. If you have a DataTemplate for it, bindings in it will use a single elements of collection boud to ItemsSource as a DataContext rather than DataContext od entire ListBox.
Thank you for reading my question.
The situation:
I have an ObservableCollection<CheckableListItem<T>> CheckableItems
The class CheckableListItem<T> has 2 elements: bool IsChecked and T Item.
The class acts as a wrapper class that adds a checkbox to each Item.
In this project the Item passed has a string element called Name.
How it is displayed in XAML code:
<ListBox ItemsSource="{Binding CheckableItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This gives me a Listbox with every entry containing a checkbox and the content of the checkbox is the Item.Name string.
The problem:
I have added a textbox in XAML <TextBox></TextBox> And Now I would like the listbox to only display the objects from the observable collection which match the text from the TextBox.
How I think it could be done:
Create a view of some kind to bind to the listbox and update the view with only the objects that match the search criteria. If no text is entered in the searchbox then all object must be displayed, If only the letter E for example is entered, only the objects containing a letter E in the Item.Name property should be displayed.
I think best would be to bind the text to a string variable in my datacontext and fire an event each time the string changes, something like this:
string SearchString
<TextBox Text="{Binding Path=SearchString, UpdateSourceTrigger=PropertyChanged}" TextChanged="TextBox_TextChanged" />
The function:
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
// Called each time the text changes, perform search here?
}
I just lack the knowledge of WPF syntax for how to create this or how to google the right terms.
Edit:
I now have an ICollectionView checkableItemsView of my ObservableCollection<CheckableListItem<T>> CheckableItems But how to filter it on the Item.Name property?
The binding works, just the filtering I need help with:
<ListBox Grid.Column="1" ItemsSource="{Binding CheckableItemView}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" Content="{Binding Path=Item.Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any input is welcome. Thanks in advance.
Rather than binding directly to your ObservableCollection, you could bind to an ICollectionView.
This would allow you to set a Filter property (using a predicate) that would filter out entries at the UI level, without changing the underlying collection.
Take a look at the filtering section of this page:
http://wpftutorial.net/DataViews.html
edited to add example of filtering:
ICollectionView _customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.Filter = CustomerFilter
private bool CustomerFilter(object item)
{
Customer customer = item as Customer;
return customer.Name.Contains( _searchString );
}
.After some more research I came with the following solution:
Credits go to Paul for pointing me in the right direction.
Source: http://jacobmsaylor.com/?p=1270
private bool CustomFilter(object item)
{
CheckableListItem<Item> checkableItem = item as CheckableListItem<Item>;
if (checkableItem != null && checkableItem.Item.Name.Contains(SearchString))
{
return true;
}
return false;
}
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
checkableItemsView.Filter = CustomFilter;
}
let me start by introducing my current setup:
I have a ListView that binds its SelectedItem property to the ViewModel, like this:
<ListView Name="FileListView" ItemsSource="{Binding ImageList}"
SelectionChanged="ImageSelectionChanged"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<view:FileListItem />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
It's item template (view:FileListItem) is the following:
<Grid MouseDown="FileListItemMouseDown" KeyDown="FileListItemKeyDown">
....
<TextBlock Name="NewNameTextBlock"
Text="{Binding NewName}"
Grid.Column="2"
Visibility="{Binding TextBlockVisibility}" />
<TextBox Name="NewNameTextBox"
Text="{Binding NewName, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="2"
Visibility="{Binding TextBoxVisibility}" />
</Grid>
The idea here is to switch on the TextBox and switch off the TextBlock when the corresponding ListView item is being edited. This works ok, but when I hit a particular key, I want the ListView to select the next item and put that item into editing mode. I catch the KeyDown event as seen above in the ItemTemplate and broadcast a message, which is caught in the DataContext of my ListView like this:
public ImageFile SelectedItem {
get { return _selectedItem; }
set { _selectedItem = value; NotifyPropertyChanged("SelectedItem"); }
}
public void SelectAndEditThisHandler (object x)
{
ImageFile file = x as ImageFile;
SelectedItem = file;
}
The result is that the selection actually changes for a split second, but then it changes back to the previous selection. I suspect some other UI elements might be handling my key-presses and doing something to change the selection back, but I can't figure out which elements and how to pinpoint them.
Any help would be greatly appreciated! Thanks!
EDIT:
As requested, the SelectionChanged handler:
private void ImageSelectionChanged(object sender, SelectionChangedEventArgs e)
{
System.Collections.IList filelist = FileListView.SelectedItems;
if (filelist.Count == 1)
{
ImageFile selectedFile = FileListView.SelectedItem as ImageFile;
Mediator.Instance.NotifyColleagues(Mediator.Operations.ImagePathSelected, selectedFile.OriginalPath);
}
}
The mediator message broadcast doesn't do anything related to these controls/this problem at all.