Why are some of my buttons disabled at runtime? - c#

When the window is opened, Abort is clickable, but all of the other buttons are not.
I put breakpoints at my getters:
public ICommand OkCommand
{
get { return _okCommand; }
}
and the button is disabled after it is called. I feel like I'm missing something obvious here. Is my binding not setup correctly? Or is it not being correctly initialized?
My ViewModel constructor:
[ImportingConstructor]
public MyViewModel(IMyViewModel view)
: base(view)
{
_okCommand = new DelegateCommand(OkHandler, IsValid);
_refreshCommand = new DelegateCommand(RefreshHandler, IsValid);
_testConnCommand = new DelegateCommand(TestConnectionHandler, IsValid);
}
My XAML for two of the buttons.
<StackPanel Orientation="Horizontal" Margin="0,14" HorizontalAlignment="Center" DockPanel.Dock="Bottom">
<Button Command="{Binding OkCommand}" Content="Ok" IsDefault="True" TabIndex="3" Margin="5,0"/>
<Button Content="Abort" IsCancel="True" TabIndex="4" Margin="0"/>
</StackPanel>

The second parameter of the DelegateCommand constructor is a Func that tells if the Command is executable. WPF uses this to determine if buttons, etc should be enabled.
I would check your IsValid Func / method whether it works okay, that is: returns true.

Related

How do I add the command binding to a dynamic button?

After extensive researching I have not found an answer to this problem. I have a list box whose ItemsSource is a collection of Button objects. When I add a button to the collection it appears properly but when clicked the command is not executed. I have already implemented RelayCommand and it is used throughout my code.
C# MVVM WPF
The View
<ListBox ItemsSource="{Binding Buttons}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="5,5,5,5"
Content="{Binding Content}"
Command="{Binding ExecuteButtonCommand}"
CommandParameter="{Binding CommandParameter}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ViewModel
public RelayCommand _executeButtonCommand;
public ICommand ExecuteButtonCommand
{
get
{
if (_executeButtonCommand == null)
_executeButtonCommand = new RelayCommand(exec => this.ButtonCommands(param));
return _executeButtonCommand;
}
}
For Testing I have this code.
public void AddButtons()
{
Buttons= new ObservableCollection<Button>();
Button btn = new Button();
btn.Content = "Generate Files";
btn.Command = "{Binding ExecuteButtonCommand}";
btn.CommandParameter = "Files";
Buttons.Add(btn);
}
But I cannot assign the Command that way. The rest of the button works correctly. So I put the Command= in the view as you see above.
If this has been answered, then I can't find it. The nearest answer is nine years old and does not work.
Thanks for looking.
What is happening is that the ListBox's DataTemplate is trying to bind to a property called ExecuteButtonCommand which doesn't exist in Button object. And then, to bind the parameter, you need to point to your view's DataContext.
Change it to:
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="5,5,5,5"
Content="{Binding Content}"
Command="{Binding Command}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.MyParameter}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
For clarification, I created a property called "MyParameter" in my ViewModel. Also, in your codebehind, change your button creation code to:
Buttons = new ObservableCollection<Button>();
Button btn = new Button();
btn.Content = "Generate Files";
btn.Command = ExecuteButtonCommand;
Buttons.Add(btn);
And your ExecuteButtonCommand to simply:
public ICommand ExecuteButtonCommand
{
get
{
if (_executeButtonCommand == null)
_executeButtonCommand = new RelayCommand(ButtonCommands);
return _executeButtonCommand;
}
}
I wanted to close this out with the final result in case someone else is searching for the same answer.
Mari set me straight which led to this example below as the final result. There is no "Code Behind." Generation of the buttons is done in the view model. After a button is created it is added to the button collection which is the source for the ListBox. I am only including the code specific to the question.
This is how it ended up.
The View
<ListBox ItemsSource="{Binding Buttons, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="AliceBlue"
BorderBrush="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectedItem="">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="5,5,5,5"
Content="{Binding Content}"
Command="{Binding Command}"
CommandParameter="{Binding CommandParameter}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ViewModel - A switch statement is used to determine what button needs to be generated. I gave the button a name because I wanted to be able to find it in the collection and set the Enabled property. But that didn't work and I still haven't found an answer.
public void AddButton(string param)
{
Button btn = new Button();
switch (param)
{
case "Files":
btn.Content = "Do Files";
btn.CommandParameter = "Files";
btn.Name = "Files";
break;
//More items here
}
btn.Command = ExecuteButtonCommand; //The ICommand name. I made this harder than it needed to be!
Buttons.Add(btn);
}
public RelayCommand _executeButtonCommand;
public ICommand ExecuteButtonCommand
{
get
{
if (_executeButtonCommand == null)
_executeButtonCommand = new RelayCommand(param => this.ButtonCommands(param));
return _executeButtonCommand;
}
}
I hope that can help someone.

Pass update parameter in WPF using MVVM

I am having a problem updating Entity while using WPF MVVM and commands.
My WPF looks like:
<Popup Margin="10,10,0,13" Name="UpdatePopup" HorizontalAlignment="Left" VerticalAlignment="Top" Width="450" Height="100" IsOpen="{Binding IsOpen, Mode=TwoWay}">
<Border Padding="5" Background="WhiteSmoke">
<StackPanel Orientation="Horizontal" DataContext="{Binding CommendationEntity}" Width="450" Height="100">
<Label Content="Nazwa żódła" Margin="10,10,10,10" Width="75" Height="30"/>
<TextBox Text="{Binding Name}" Margin="10,10,10,10" Width="130" Height="30" x:Name="Name" />
<Button Command="{Binding DataContext.UpdateCommand, ElementName=UpdatePopup}" CommandParameter="{Binding id}" Content="Update" Margin="10,10,10,10" Width="80" Height="30"/>
<Button Command="{Binding DataContext.CancelCommand, ElementName=UpdatePopup}" Content="Anuluj" Margin="10,10,10,10" Width="80" Height="30"/>
</StackPanel>
</Border>
</Popup>
Now to update record I need id and new name so I am passing id with button binding, and I am having trouble passing name, my update method looks like:
public void UpdateEntity(object obj)
{
this.CommendationEntity = this._catalog.Commendations.Single(entity => entity.id == (int)obj);
this.CommendationEntity.Name = this.Name;
this._catalog.Commendations.Attach(this.CommendationEntity);
this._catalog.Entry(this.CommendationEntity).State = System.Data.Entity.EntityState.Modified;
this._catalog.SaveChanges();
}
And in the view model I have a property named:
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
this._name = value;
RaisePropertyChanged("Name");
}
}
But when I click Update id is passed as (object obj), with is right, but Name property does not update, what could be wrong?
Maybe its data context (DataContext="{Binding CommendationEntity}") as model name and view model property has same name? I'm new WPF so I can be wrong.
Or maybe there is a way to just click button and whole object will be passed as (object obj)?
this.Name indicates that the Name property belongs to the same class as the UpdateCommand property and then the path of the binding to the Name property should be set up the same way:
<TextBox Text="{Binding DataContext.Name, ElementName=UpdatePopup}" Margin="10,10,10,10" Width="130" Height="30" x:Name="Name" />
Otherwise you are binding to some other Name property or the binding simply fails.
Two possibilities:
Is the binding for Name correct? For the commands you are using DataContext.UpdateCommand, ElementName=UpdatePopup so it may be you need to do this as well. If the binding fails you don't get an exception but you should get a message in the output window of VS - worth checking there.
If for some reason in the Name setter: if (_name == value) (perhaps both are emptry strings?) is true then the name won't update.
As per you code you are trying to bind Name property of CommendationEntity with textbox control.It is not getting updated because you have not implemented NotifyPropertyChanged for this.CommendationEntity.Name property. You need to implement INotifyProperty at CommendationEntity to reflect the property changed of CommendationEntity class.

Can I swap a buttons content databind to a different databind in code?

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",...};
}

The call is ambiguous between the following methods or properties - WPF

I have a WPF form that has some buttons to save user input, delete, and cancel. I was trying to add functionality such that whenever the user clicks the cancel button, there is a pop up a message. Instead it throws an exception in my controller:
"The call is ambiguous between the following methods or properties"
Here is my view:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="0,0,0,0">
<Button Name="DeleteButton" Command="{Binding DeleteCommand}" CommandParameter="{Binding Path=SelectedStory}" Cursor="Hand" Height="25" IsEnabled="{Binding CanDelete}" Margin="5 0 0 0">Delete</Button>
<Button Name="SubmitButton" Command="{Binding SubmitCommand}" CommandParameter="{Binding Path=SelectedStory}" Cursor="Hand" Height="25" Margin="5 0 0 0">Submit</Button>
<Button Name="CancelButton" Command="{Binding CloseCommand}" CommandParameter="{Binding Path=SelectedStory}" Cursor="Hand" Height="25" Margin="5 0 0 0" >Cancel</Button>
</StackPanel>
My controller code:
public MetadataController(IGatewayService gateway, IEventAggregator eventAggregator, IDialogService dialog)
{
this.gateway = gateway;
this.eventAggregator = eventAggregator;
this.dialog = dialog;
// commands
this.CloseCommand = new DelegateCommand<StoryItem>(this.Close);//here i got the exception throwing "the call is ambiguous between the following methods or properties"
this.DeleteCommand = new DelegateCommand<StoryItem>(this.Delete);
this.SubmitCommand = new DelegateCommand<StoryItem>(this.Submit, this.HasFieldsRequiredBeforeSubmit);
this.eventAggregator.GetEvent<StorySelectedEvent>().Subscribe(OnStorySelected);
}
private void Close(StoryItem selsectedStory)//when i click my close button its not calling this method at all.
{
bool isConfirmed = this.dialog.ShowConfirmation("Are you sure you want to close?");
}
private void Delete(StoryItem selectedStory)
{
bool isConfirmed = this.dialog.ShowConfirmation("Are you sure you want to permanently delete ?");
if (isConfirmed)
{
this.gateway.DeleteStoryItem(selectedStory);
this.eventAggregator.GetEvent<CommandCompletedEvent>().Publish(CommandTypes.MetadataEntry);
}
}
The exception you're getting indicates that it doesn't how to access whatever method/property you're attempting to call. Perhaps there is some other method or property that is also called Close or CloseCommand and is causing the conflict?
The reason for the exception ,i do have the method already and i was trying to create one,that's why it throws that error.Thanks lot for the help guys.

How can I raise a custom Routed Event from a DataTemplate?

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 ;)

Categories

Resources