I'm making a user-control which is called "FileSelector" based on listbox. The list box is filled with an observablecollection "FileDisplay" which contains file names selected from a dialog.
<ListBox x:Name="FileListBox" Template="{DynamicResource BaseListBoxControlStyle}" Grid.RowSpan="5" Grid.Row="1" Margin="0" ItemContainerStyle="{DynamicResource BaseListBoxItemStyle}" ItemsSource="{Binding DataContext.FileDisplay, ElementName=F_Selector, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<DockPanel>
<Button x:Name="ListDelete" Width="{Binding ActualHeight, ElementName=ListDelete}" Style="{DynamicResource BaseButtonStyle}" Margin="4,0,0,0" DockPanel.Dock="Right" Content="X" Click="FileDelete_Click"/>
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal">
<ToggleButton x:Name="ListCheck" Width="{Binding ActualHeight, ElementName=ListCheck}" Style="{DynamicResource BaseToggleButtonStyle}" Margin="0,0,4,0" Checked="File_Checked" Unchecked="File_Unchecked" />
<TextBlock Text="{Binding ., Converter={StaticResource PathToFileName}}" TextTrimming="CharacterEllipsis"/>
</StackPanel>
</DockPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When a toggle button contained in the item is checked, I want to add the content of the item to a dependencyproperty of type observablecollection.
private void File_Checked(object sender, RoutedEventArgs e)
{
ToggleButton btn = (ToggleButton)sender;
int index = FileListBox.Items.IndexOf(btn.DataContext);
FileChecked[index] = true;
FileSelected.Add(FileDisplay[index]);
}
The dependencyproperty :
public static readonly DependencyProperty FileSelectedProperty =
DependencyProperty.Register("FileSelected", typeof(ObservableCollection<string>), typeof(FileSelector));
[Bindable(true)]
public ObservableCollection<string> FileSelected
{
get { return (ObservableCollection<string>)this.GetValue(FileSelectedProperty); }
set { this.SetValue(FileSelectedProperty, value); }
}
Also, FileChecked is an observablecollection to keep track of which element is checked or not for later use.
Everything compiled fine but when I check one of the togglebutton this error is thrown :
Access violation at address 00007FFA2981CC85. Read of address 0000000000000000.
It is worth noting that if I changed the DependencyProperty FileSelected to a simple observablecollection, there is no problem, but I don't want to do that as I could not bind it later on.
Any idea why ? thank you
EDIT----
After some more testing with visualstudio debugger, I found that whenever I click on a togglebutton, the dependencyproperty named "FileSelected" is null, while it should be added "FileDisplay[index]" even if I replace the latest by any kind of string...
I'm a little confused. You say this:
It is worth noting that if I changed the DependencyProperty FileSelected to a simple observablecollection, there is no problem, but I don't want to do that as I could not bind it later on.
But in your sample, FileSelected IS an ObservableCollection. What data type was FileSelected originally intended to be?
After some more testing with visualstudio debugger, I found that whenever I click on a togglebutton, the dependencyproperty named "FileSelected" is null, while it should be added "FileDisplay[index]" even if I replace the latest by any kind of string...
There's no way of telling why FileSelected is null without knowing its true data type. Is there only one file selected at a time or multiple?
If FileSelected is supposed to be an OC, it's probably null because you haven't a) initialized it anywhere, or b) you didn't initialize it properly.
Can you post a sample of your initialization?
Related
I've added a MenuFlyout to a button in ItemsControl.ItemTemplate. Also I was able to bind current item as CommandParameter.
Now I want to bind Command to a MenuFlyoutItem.
In codebehind :
LayoutRoot.DataContext = this;
So if i bind to LayoutRoot I will actually bind to my current UserControl. But the following binding is not working:
Command="{Binding ActivateProfileCommand, ElementName=LayoutRoot}"
It gives me not errors in Output but it's not working.
Here's the full example:
<controls:HeaderDecorator x:Uid="AccountsHeader" Text="Accounts" x:Name="LayoutRoot" Name="LayoutRoot">
<controls:HeaderDecorator.Resources>
<MenuFlyout x:Key="AccountMenuFlyout">
<MenuFlyoutItem Text="Activate" Name="Activate"
Command="{Binding ActivateProfileCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}" />
</MenuFlyout>
</controls:HeaderDecorator.Resources>
<StackPanel Orientation="Vertical">
<ItemsControl ItemsSource="{Binding Settings.Profiles}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<HyperlinkButton Content="{Binding}" FlyoutBase.AttachedFlyout="{StaticResource AccountMenuFlyout}" >
<i:Interaction.Behaviors>
<ic:ShowFlyoutBehavior />
</i:Interaction.Behaviors>
</HyperlinkButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</controls:HeaderDecorator>
Seems the problem is i'm trying to use shared object in Resources. Can I do it? And why not?
The issue you are seeing here is the MenuFlyoutItem is no longer in the datacontext you perhaps think it is. I'll try and explain this as best I can as a few I know who work with xaml have come across this and hit their heads off walls for days about it. It's also known to not show errors in your particular scenario; further increasing confusion.
In a nutshell. When the MenuFlyout is added inside the ItemTemplate of each item in your collection, it does not have access to the datacontext you perhaps think it does. In this case, the datacontext that the control now resides is actually the individual item within the collection it is sitting in.
There is however a solution to this. I have something similar to yourself. An ItemsControl which has it's ItemsTemplate defined that includes a UIElement who's FlyoutBase AP references a MenuFlyout defined in a resource dictionary.
The xaml is pretty much the same except I don't need the ElementName in the binding.
However, I have now turned my attention to the type that the collection holds. I have code that looks something like this.
public class AnItemToList
{
public AnItemToList(Action commandDel)
{
TestCommand = new RelayCommand(commandDel);
}
public string Name { get; set; }
public RelayCommand TestCommand { get; set; }
}
Note that the command is being defined in the item itself and that I'm passing the method that the command will execute via the constructor.
All I have to do for the command binding on the MenuFlyoutItem is
<MenuFlyoutItem Text="Activate"
Name="Activate"
Command="{Binding TestCommand}"/>
I don't have a command param set here as I just quickly put together a basic template Windows Phone app and the pre-packed ICommand implementation doesn't have a delegate set to take the param.
If you now stick a break point in the method the command is calling, you'll see it will be called from any of the MenuFlyoutItem's bound to the command that references it.
Bare in mind that this isn't the only way of solving this problem; but it is one I use myself on occasion. For example, in WPF XAML you can make use of RelativeSource to go looking for the command on a parent control's datacontext.
Hope this helps.
Here's a general "Pair" object:
public class Pair : DependencyObject
{
public static readonly DependencyProperty FirstProperty = DependencyProperty.Register("First",
typeof(object), typeof(Pair), new PropertyMetadata(null));
public static readonly DependencyProperty SecondProperty = DependencyProperty.Register("Second",
typeof(object), typeof(Pair), new PropertyMetadata(null));
public object First
{
get { return GetValue(FirstProperty); }
set { SetValue(FirstProperty, value); }
}
public object Second
{
get { return GetValue(SecondProperty); }
set { SetValue(SecondProperty, value); }
}
}
In ItemTemplate i put something like this:
<DataTemplate>
<Grid>
<Grid.Resources>
<viewModel:Pair x:Key="Tuple" First="{Binding DataContext, ElementName=LayoutRoot}"
Second="{Binding}" />
</Grid.Resources>
<HyperlinkButton Content="{Binding Second.ProfileName}"
DataContext="{StaticResource Tuple}"
FlyoutBase.AttachedFlyout="{StaticResource AccountMenuFlyout}"
</HyperlinkButton>
</Grid>
</DataTemplate>
Now I can easily reference Tuple elements from my Resource like this:
<MenuFlyoutItem Text="Activate" Name="Activate"
Command="{Binding First.ActivateProfileCommand}"
CommandParameter="{Binding Second}" />
I have a ListView that looks like this, that controls which tab in my application that is opened.
<ListView Grid.ColumnSpan="2" Grid.Row="1" SelectedItem="{Binding Path=SelectedSubstanceName}" Name="listView" ItemsSource="{Binding Path=Substances}" HorizontalAlignment="Stretch" Margin="2" VerticalAlignment="Stretch">
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="Lägg till" Command="{Binding AddSubstanceCommand}"/>
<MenuItem Header="Ta bort" Command="{Binding RemoveSubstanceCommand}"/>
</ContextMenu>
</ListView.ContextMenu>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I use the SelectedSubstanceName property to detect which tab to open, or switch to, if it's already open.
The property looks like this:
private SubstanceName selectedSubstanceName;
public SubstanceName SelectedSubstanceName
{
get
{
return selectedSubstanceName;
}
set
{
selectedSubstanceName = value;
OnPropertyChanged("SelectedSubstanceName");
if (selectedSubstanceName != null)
{
if (!Tabs.Any(t => t.Identify(selectedSubstanceName.SubstanceNameID, typeof(SubstanceTabsViewModel))))
AddTab(selectedSubstanceName);
else
SelectedTab = Tabs.First(t => t.Identify(selectedSubstanceName.SubstanceNameID, typeof(SubstanceTabsViewModel)));
}
}
}
The case I'm not able to cover is when the user clicks "someSubstance", the corresponding tab is opened, the user closes it, and "someSubstance" is still selected. If the user wants to open it again, he has to select some other substance (which will then be opened), and then click "someSubstance" again. Is it possible to trigger the property even when clicking the same ListViewItem?
I know I could add an event on double-click, but ideally, I want to avoid both events and double-clicks.
I think the problem is that after clicking an item the first time the list's SelectedItem gets set. After clicking the same item the second time SelectedItem won't change because it is already set to that item. What you should do is set the SelectedItem to null after handling the click.
Try to unselect all Items in your ListView after the tab is closed.
YOURLISTVIEW.UnselectAll();
So the next time someone selects an Item there will be a change.
You don't actually want to use the ListView class, but instead simply use the ItemsControl, since it is the most basic way of representing a sequence of elements, but without the extras such as SelectedItem, SelectedValue, etc. that any class deriving from Selector has.
From there, it's merely a matter of how to represent each item in the ItemsControl. The behavior you want is to know when a specific item has been clicked on, which would make the Button class a good candidate, since it handles click behavior through an ICommand interface. Obviously, since you know about DataTemplates and styling in general, you should already know that you can customize how the button looks (visually) without sacrificing the actual behavior (click-handling).
<ItemsControl ItemsSource="{Binding Path=Substances}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource SomeStyleToChangeItsLook}"
Command="{Binding Path=SelectSubstanceCommand}"
CommandParameter="{Binding}"
Content="{Binding Path=Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public ICommand SelectSubstanceCommand { get; private set; }
private void SelectSubstance(object parameter)
{
// Add the substance that was "clicked" on here however you want to do it.
}
Keep in mind I don't know what framework you are using, so I just gave a general example of how the Command code might look in your view-model. The key to MVVM and using WPFs awesome UI is to always think of what behavior you want and which controls offer that behavior. Ignore how they actually look because that can be changed without losing that behavior.
I've been having some trouble in my silverlight app, and am hoping one of you may help me. :) I am attempting to set up a datatemplate for a datagrid but am having some trouble with a binding. It seems that when I set my binding mode to "TwoWay", I get the following error:
Note that I am trying to bind to the DataContext that is automatically "assigned" to the datatemplate
Provide value on 'System.Windows.Data.Binding' threw an exception.
Can anyone provide me with a solution to this problem? My objective is to have the datatemplate in a separate view to allow greater customization to my datagrid. The following is a dumbed down version of what I am attemping:
<sdk:DataGrid ItemsSource="{Binding items}" SelectedItem="{Binding selItem, Mode=TwoWay}">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Width="*">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<View:leadDataTemplate item="{Binding Mode=TwoWay}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
Where <View:leadDataTemplate item="{Binding Mode=TwoWay}"/> would be the view I am attempting to initialize.
Hopefully you get the idea, thanks in advance for any suggestions or answers!
Edit:
Note that item is the dependency property, and it doesn't matter if the source is updated when changes are made to the item, I simply need the datagrid to display information within the item, so I require the dependency property to be set, thanks!
Second edit:
I believe the problem may be that my Binding isn't passing any data at all to the view, do you know why this would be? I know that the items section is being filled, so why would the binding be empty? Also, please note that the dependency property is supposed to update another viewmodel as follows:
public static readonly DependencyProperty itemProperty = DependencyProperty.Register("item", typeof(object), typeof(leadDataTemplate), new PropertyMetadata(new PropertyChangedCallback(itemChanged)));
public leadStreamData item
{
get { return (object)GetValue(itemProperty); }
set { SetValue(itemProperty, value); }
}
private static void itemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var ctrl = (leadDataTemplate)d;
if (ctrl != null)
ctrl._vm.item = ctrl.item;
}
TwoWay binding is only required when you wish to update the source data.
The following should suffice:
<sdk:DataGrid ItemsSource="{Binding items}" SelectedItem="{Binding selItem, Mode=TwoWay}">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Width="*">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<View:leadDataTemplate item="{Binding .}"/>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
To use TwoWay binding, you must provide a Path property e.g.
<TextBox Text="{Binding FirstName, Mode=TwoWay}" />
Hopefully this is an easy question. I'm using the ListBoxDragDropTarget from the Silverlight Toolkit to set up drag and drop from one ListBox to another. I can't seem to get the event to fire. Here's my XAML code:
<toolkit:ListBoxDragDropTarget HorizontalAlignment="Left"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Top"
VerticalContentAlignment="Stretch"
Margin="39,117,0,0"
Grid.Row="1"
AllowDrop='True'>
<ListBox x:Name='columnHeadings'
MinHeight='100'
MinWidth='100'>
</ListBox>
</toolkit:ListBoxDragDropTarget>
<toolkit:ListBoxDragDropTarget AllowDrop='True'
Grid.Column='1'
Grid.Row='1'
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Center"
HorizontalAlignment="Left">
<ListBox x:Name='customerFields'
Grid.Column='1'
Grid.Row='1'
Visibility='Collapsed'
Drop='CustomerFieldsDrop'>
</ListBox>
</toolkit:ListBoxDragDropTarget>
And here's my event handler in the code behind page:
private void CustomerFieldsDrop(object sender, DragEventArgs e)
{
MessageBox.Show(string.Format("Something was dropped!"));
}
As you can see, I was aiming for something real simple. I tried assigning an event handler to the parent ListBoxDragDropTarget for the customerFields List Box; ironically, that worked.
My purpose here is to allow a user to import a text file and get a listing of the file's column headers in one List Box and then "connect" them to data fields listed in the second List Box. So no list reordering or moving items from one list to another.
The columnHeadings.ItemsSource property is a string[] object. The customerFields.ItemsSource property is an IEnumerable<string> object.
Any insight would be appreciated.
I think the AllowDrop="True" and Drop="EventName" properties need to be within the same element to work. You probably have to set the AllowDrop property to "True" in the ListBox itself:
<ListBox x:Name="customerFields"
Grid.Column="1"
Grid.Row="1"
Visibility="Collapsed"
Drop="CustomerFieldsDrop"
AllowDrop="True"
</ListBox>
Or add the Drop="CustomerFieldsDrop" property to the ListBoxDragDropTarget tag:
<toolkit:ListBoxDragDropTarget AllowDrop='True'
Grid.Column='1'
Grid.Row='1'
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Drop="CustomerFieldsDrop">
Either one should work...
How can I bind event handler to the custom object that i created?
here is my XAML
<ListBox x:Name="ListData">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,30">
<Image VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Path=TileImage}" Width="175" Height="175" />
<TextBlock Margin="5" Width="200" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Text="{Binding Path=TileName}" FontSize="25"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code Behind
// Create collection
ImageTiles = new ObservableCollection<ImageTile>();
// Create each object in the collection
ImageTile RSS= new ImageTile("RSS", "/Images/Hard.jpg");
ImageTile test= new ImageTile("test", "/Images/Hard.jpg");
ImageTile Exam= new ImageTile("Exam", "/Images/Hard.jpg");
ImageTile Settings = new ImageTile("Settings", "/Images/Hard.jpg");
ImageTiles.Add(RSS);
ImageTiles.Add(test);
ImageTiles.Add(Exam);
ImageTiles.Add(Settings);
this.ListData.ItemsSource = ImageTiles;
I would like to bind the event handler along with each ImageTile. Any idea how to do so? =)
Based on your code structure I'll answer assuming you're not using MVVM or the like, however I'd definitely recommend that pattern for Silverlight development.
Nevertheless, your datasource binding would be something like:
<ListBox x:Name="ListData" ItemsSource="{Binding ImageTiles}">
</ListBox>
You could create a single generic click handler in your code behind and assign the Image's click event to that handler.
<ImageButton VerticalAlignment="Top" HorizontalAlignment="Left" Source="{Binding Path=TileImage}" Width="175" Height="175" Click="imageButton_Click" />
You could then have a method on your object responsible for redirecting to whatever necessary place for that particular image tile. Then in your code behind handler would resemble this:
private void imageButton_Click(object sender, RoutedEventArgs e)
{
var imageTile = ((ImageButton)sender).DataContext as ImageTile;
imageTile.RedirectSomewhere();
}
In your ViewModel, add a property to capture selected item (e.g.)
private ImageTile _selectedItem;
public ImageTile SelectedItem
{
get {return _selectedItem;}
set
{
if(value != _selectedItem)
{
_selectedItem = value;
RaisePropertyChanged("SelectedItem");
}
}
}
Then, in your XAML, bind the SelectedItem:
<ListBox ItemsSource="{Binding ImageTiles}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
</ListBox>
Use MVVM Light (or some other way) to bind the SelectionChanged event to a command, or wire up an event handler for the SelectionChanged event. In your Command (or event handler), get SelectedItem, and look at the first property (you didn't tell us what it was called, so I don't know what to call it).
Remember to make sure that SelectedItem is not null before you do anything, and, when you're done handling the command, set SelectedIndex to -1, so that they can select the same item twice, and still get the functionality to execute.