I have a User Control,called dCB_Props that contains several objects, most importantly a ComboBox that's bound to an Observable Collection. Though the collection can take any object, it will normally take a UserControl called EditDeleteItem. I've set dCB_Props to use EditDeleteItem as an ItemsTemplate but the events aren't fired. If, on the other hand, I add an instance of EditDeleteItem then the events will get fired. I can't add items this way because the EditDeleteItem will host other controls and I'd need to use different DataTemplates.
EditDeleteItem has two Routed Events called EditClick and DeleteClick.
When the collection changes it fires an event that checks if the item added is of type EditDeleteItem. If so, then it adds handlers to the two aforementioned events.
Part of the xaml for EditDeleteClick:
<WrapPanel x:Name="wp" HorizontalAlignment="Right" Visibility="Hidden" VerticalAlignment="Center" Margin="0,0,5,0">
<Button x:Name="PART_Edit" Width="20" Height="20" Content="{DynamicResource dPen}" Style="{DynamicResource dTranspButton}" Click="btnEdit_Click"/>
<Button x:Name="PART_Delete" Width="20" Height="20" Content="{DynamicResource dCross}" Style="{DynamicResource dTranspButton}" Click="btnDelete_Click"/>
</WrapPanel>
<Label Content="{TemplateBinding Content}" Margin="2,0,45,0" Padding="0,0,0,0" HorizontalAlignment="Left" VerticalContentAlignment="Center"/>
Part of the xaml for dCB_Props:
<ComboBox HorizontalContentAlignment="Stretch" x:Name="PART_cb" Background="Transparent" Margin="0,0,0.367,0" d:LayoutOverrides="HorizontalAlignment" ItemsSource="{Binding Items, ElementName=dcb}" IsDropDownOpen="{Binding IsDropDownOpen,ElementName=dcb, Mode=TwoWay}" Grid.ColumnSpan="3" Style="{DynamicResource DaisyComboBox}" />
<Button x:Name="PART_Edit" Width="20" Height="20" Content="{DynamicResource dPen}" Visibility="Hidden" Style="{DynamicResource dTranspButton}" Margin="2.581,1.48,17.778,-1.48" Grid.Column="1" Click="btnEdit_Click"/>
<Button x:Name="PART_Delete" Width="20" Height="20" Content="{DynamicResource dCross}" Visibility="Hidden" Margin="22.602,1.48,-2.243,-1.48" Style="{DynamicResource dTranspButton}" Grid.Column="1" Click="btnDelete_Click"/>
<Button x:Name="PART_Add" Content="+" Grid.Column="3" Margin="0,0,0,0" Style="{DynamicResource dTranspButton}" Click="btnAdd_Click"/>
Note the above two are codes just for objects, I've left out Column Definitions, Event Triggers, etc.
Part of dCB_Props.xaml.cs code is:
public partial class dCB_Props : UserControl
{
public dCB_Props()
{
this.InitializeComponent();
Items= new ObservableCollection<object>();
Items.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(Items_CollectionChanged);
}
void Items_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (var o in e.NewItems)
{
if (o.GetType() == typeof(EditDeleteItem))
{
EditDeleteItem itm = (EditDeleteItem)o;
itm.EditClick += new RoutedEventHandler(ItemEdit_Click);
itm.DeleteClick += new RoutedEventHandler(ItemDelete_Click);
}
}
}
}
...//I've left some code here since I don't deem it's that important for the situation
private void ItemEdit_Click(object sender, RoutedEventArgs e)
{
DependencyObject d = GetTemplateChild("PART_cb");
if (d == null) return;
ComboBox cb = (ComboBox)d;
if (cb.SelectedItem != null) RaiseEvent(new RoutedEventArgs(EditClickEvent, e.OriginalSource));
}
}
The above works if I add an item of type EditDeleteItem and remove the ItemTemplate property for the Label that resides inside dCB_Props. It also works if I set the ItemTemplate, shown below, in EditDeleteItem's ContentTemplate. But, as mentioned, I need to use different Data Templates so I assume all Data Templates will have to reside in a Resource Dictionary and then I'd have to use a Template Selector.
Data Template:
<DataTemplate x:Shared="false" x:Key="TagTemplate">
<local:EditDeleteItem x:Name="edItem">
<local:EditDeleteItem.Content>
<StackPanel>
<TextBlock Text="{Binding Path=Content.Label}"/>
<CheckBox Content="Isolated" IsChecked="{Binding Content.IsIsolated}"/>
<CheckBox Content="Match Case" IsChecked="{Binding Content.MatchCase}"/>
<CheckBox Content="Include" IsChecked="{Binding Content.Include}"/>
</StackPanel>
</local:EditDeleteItem.Content>
</local:EditDeleteItem>
</DataTemplate>
I believe I need to use command bindings. But not really sure where to put the CommandBindings, and not so sure how to use them, though I've read a page or two.
Thanks,
Hassan
The events are fired, but you don't catch them, because subscription in Items_CollectionChanged never occurs if ItemTemplate is used.
You should understand how ItemsControl (and ComboBox) works with ItemsSource. ItemsControl use ItemContainerGenerator to populate its visual tree. Each item from ItemsSource wrap into container which derived from ContentControl. Then item is set as a Content, ItemTemplate is set as ContentTemplate and so on. When you put EditDeleteItem into ItemTemplate it becomes a part of visual tree but not an item. That's why there is no EditDeleteItem in e.NewItems and no subscription.
The right way is Commands, as you mentioned. You should declare two commands:
public class EditDeleteItem : UserControl
{
...
public static readonly RoutedUICommand EditCommand = new RoutedUICommand(...);
public static readonly RoutedUICommand DeleteCommand = new RoutedUICommand(...);
...
}
Now the part of template may look like:
<WrapPanel ...>
<Button ... Command="{x:Static EditDeleteItem.EditCommand}"/>
<Button ... Command="{x:Static EditDeleteItem.DeleteCommand}"/>
</WrapPanel>
Then you add command bindings to dCB_Props:
public partial class dCB_Props : UserControl
{
static dCB_Props()
{
...
CommandManager.RegisterClassCommandBinding(
typeof(dCB_Props),
new CommandBinding(EditDeleteItem.EditCommand, OnEditCommandExecuted));
CommandManager.RegisterClassCommandBinding(
typeof(dCB_Props),
new CommandBinding(EditDeleteItem.DeleteCommand, OnDeleteCommandExecuted));
...
}
...
}
You need to implement OnEditCommandExecuted and OnDeleteCommandExecuted in order to handle corresponding commands from EditDeleteItem.
I hope I understood your question correctly ;)
Related
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));
I am working on a Win10 UWP app using MVVMLight (I've never used MVVMLight, and never done commands "properly"). I have an ItemsControl bound to an ObservableCollection. The Participant has two properties - Name and Laps. I have controls in the ItemsControl.ItemTemplate to display a button (subtract), the Name property of the item, the Laps property of the item, and another button (add). Here is the XAML:
<ItemsControl
ItemsSource="{Binding Participants, Mode= TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid
MinWidth="300"
Margin="0, 12">
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="2*"></ColumnDefinition>
<ColumnDefinition
Width="6*"></ColumnDefinition>
<ColumnDefinition
Width="2*"></ColumnDefinition>
<ColumnDefinition
Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button
Content=""
FontFamily="Segoe MDL2 Assets"
FontSize="20"
Grid.Column="0"
Margin="0, 0, 12, 0"></Button>
<TextBlock
Text="{Binding Path=Name, Mode=TwoWay}"
FontSize="20"
FontWeight="Bold"
Grid.Column="1"></TextBlock>
<TextBlock
Text="{Binding Laps, Mode=TwoWay}"
FontSize="20"
FontWeight="Bold"
Grid.Column="2"></TextBlock>
<Button
Content=""
FontFamily="Segoe MDL2 Assets"
FontSize="20"
Command="{Binding AddLapCommand}"
CommandParameter="{Binding}"
Grid.Column="3"
Margin="12, 0, 0, 0"></Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The view model creates an ObservableCollection called Participants. I am trying to bind the add button (and then of course the subtract button) to a RelayCommand in the VM. I've tried a number of things, so I can't post all I've tried here. Here is the latest of what I have, (but it still doesn't work):
public RelayCommand<object> AddLapCommand
{
get
{
if (_addLapCommand == null)
{
_addLapCommand = new RelayCommand<object>((e) => ExecuteAddLapCommand(e));
}
return _addLapCommand;
}
}
private void ExecuteAddLapCommand(object o)
{
throw new NotImplementedException();
}
The intellisense in the xaml tells me that it cannot resolve the property AddLapCommand in data context of type LapCounter.Model.Participant. I'm not trying to get to the Participant class, but to the HomeViewModel class, which is the DataContext of the page. I think I can see where it is getting the LapCounter.Model.Participant from, from the individual item in the collection that the ItemsControl is bound to. But I was under the impression that if a DataContext couldn't be found, it would continue up the visual tree until it found the right one. Here's the DataContext in the page declaration:
DataContext="{Binding Home, Source={StaticResource Locator}}"
How do I get it to look at the VM for the RelayCommand? What I need to do is have the button send the Participant that it represents to the RelayCommand as a parameter, and use that to increment (and decrement in the case of the subtract button) the Laps integer for that particular Participant.
I appreciate any help I can get. Thanks!
EDIT
Added Participants property from the VM to show because my view is not updating. Here is the Participants property:
/// <summary>
/// The <see cref="Participants" /> property's name.
/// </summary>
public const string ParticipantsPropertyName = "Participants";
private ObservableCollection<Participant> _participants = new ObservableCollection<Participant>();
/// <summary>
/// Sets and gets the Participants property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public ObservableCollection<Participant> Participants
{
get
{
return _participants;
}
set
{
if (_participants == value)
{
return;
}
_participants = value;
RaisePropertyChanged(() => Participants);
}
}
Thanks to Eldar Dordzhiev for his help so far!
Try this:
Command="{Binding Home.AddLapCommand, Source={StaticResource Locator}}"
There's not much to explain to you as long as everything you've written is absolutely right.
As for incrementing\decrementing the Laps, you can simply pass the Participant into the command handler and alter the Laps. If you use MVVMLight that is done with RelayCommand<Participant>. Don't forget to pass the Participant in CommandParameter.
XAML code:
<TextBlock Text="Country" Foreground="white" TextAlignment="Right" Grid.Row="2" Grid.Column="0" />
<TextBox
x:Name="txtCountries"
Grid.Row="2"
Grid.Column="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
Margin="2, 2, 2, 2"
Text="{Binding PhysicalDeliveryParameters.Countries, Converter={StaticResource EnumerableToTextConverter}, ConverterParameter='...'}"
IsReadOnly="True">
</TextBox>
<Button
Grid.Row="2"
Grid.Column="3"
Content="..."
HorizontalAlignment="Left"
Tag="Countries"
Click="ButtonBase_OnClick" />
C# code :
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
PhysicalDeliveryParametersViewModel pvm = GetViewModel();
GenericObservableCollection<SelectableItem> items = pvm.Countries;
PhysicalDeliveryParametersDlg dlg = new PhysicalDeliveryParametersDlg(items);
dlg.Closed += (o, args) =>
{
BindingExpression binding = txtCountries.GetBindingExpression(TextBox.TextProperty);
if(null != binding)
binding.UpdateSource();
};
dlg.ShowDialog();
}
When I click on the button, the ButtonBase_OnClick() method executes : a dialog appears (PhysicalDeliveryParametersDlg class) and I choose some values. The binded data (PhysicalDeliveryParameters.Countries, which is an ObservableCollection) is updated, but not the Text property of my TextBox... Did I do something wrong ?
PS : I'm not sure I use the best method to create a modal window in Silverlight, could you give me some advice ?
It looks like the problem is that PropertyChanged never gets raised on the "Countries" property, so the view doesn't know it needs to update. (Actually, it probably wouldn't help to raise "PropertyChanged" in this case -- since the object reference has not changed, I believe the runtime would ignore it.)
I would just add another property "CountriesString" or similar:
Text="{Binding PhysicalDeliveryParameters.CountriesString}"
Update the property whenever is appropriate:
dlg.Closed += (o, args) =>
{
pvm.CountriesString = string.Join(", ", pvm.Countries);
};
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>
...
I cannot find any examples to make me understand how and if I can change the databind in c# at the click of a button on, in my case a toggleswitch, Basically I have 32 buttons in my app and those 32 buttons act the same but need different text with-in them depending on some toggle switches they are currently databinded so the text can be saved and retrieved from local storage but what values it gets depends on the state of these toggle switches.
So I currently have :
<Button x:Name="_ovButton1" Content="{Binding Source={StaticResource AppSettings}, Path=ovName1_1Value, Mode=TwoWay}" Margin="2,0,250,0" VerticalAlignment="Top" FontSize="14" Height="72" FontWeight="Bold" MouseLeftButtonUp="_ovButton1_MouseLeftButtonUp" MouseLeftButtonDown="_ovButton1_MouseLeftButtonDown" ClickMode="Hover" Hold="_ovButton1_Hold"/>
and I want when a user changes the state of a toggleswitch to change the
{StaticResource AppSettings}, Path=ovName1_1Value, Mode=TwoWay}
to for example:
{StaticResource AppSettings}, Path=ovName1_2Value, Mode=TwoWay}
but I cannot find any example that shows how to do that in c#
what code do I need to do that?
You can specify the target of databinding in code like this:
MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");
myBinding.Source = myDataObject;
myText.SetBinding(TextBlock.TextProperty, myBinding);
See more at http://msdn.microsoft.com/en-us/library/ms742863.aspx
-- Edit Note I don't have access to a WP8 Emulator to test this ---
In the view model it looks like this:
public List<string> Members
{
get { return _Members; }
set { _Members = value; OnPropertyChanged(); }
}
public MainVM()
{
// Simulate Asychronous access, such as to a db.
Task.Run(() =>
{
Thread.Sleep(2000);
Members = new List<string>() {"Alpha", "Beta", "Gamma", "Omega"};
});
}
The code behind on the main page sets the datacontext (shared with all the child controls) as such:
public MainWindow()
{
InitializeComponent();
// Set the windows data context so all controls can have it.
DataContext = new MainVM();
}
The Mainpage Xaml to bind to members is like this
<Button Height="30"
Width="80"
Margin="10"
DataContext="{Binding Members}"
Content="{Binding Path=[0] }" />
<Button Height="30"
Width="80"
Margin="10"
DataContext="{Binding Members}"
Content="{Binding Path=[1] }" />
<Button Height="30"
Width="80"
Margin="10"
DataContext="{Binding Members}"
Content="{Binding Path=[2] }" />
<Button Height="30"
Width="80"
Margin="10"
DataContext="{Binding Members}"
Content="{Binding Path=[3] }" />
The result is this visually:
I based this on my blog article Xaml: ViewModel Main Page Instantiation and Loading Strategy for Easier Binding for more info and a fuller example.
I think your best bet is going to be to use a collection of strings and bind to that collection. You can either change the collection when a toggle is switched, or keep 6 collections and bind to the collection that is for the toggle.
Xaml:
<ItemsControl x:Name="Buttons" ItemsSource="{Binding ButtonTextCollection}">
<ItemsControl.ItemsPanel>
<toolkit:WrapPanel/>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Width="100" Height="70" Content="{Binding}" Click="OnButtonClick"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Your code-behind would have the event handler for your button click
private void OnButtonClick(object sender, RoutedEventArgs e)
{
var text = ((Button) sender).Content.ToString();
// Send the text
}
Your ViewModel would hold the ButtonTextCollection property and would change based on the toggle.
public ICollection<string> ButtonTextCollection
{
get { return _buttonTextCollection; }
set
{
_buttonTextCollection = value;
OnPropertyChanged("ButtonTextCollection");
}
}
When you want to change the text, you would change the ButtonTextCollection
public void ChangeButtonText()
{
ButtonTextCollection = new Collection<string> {"A", "B",...};
}