Im new in wpf programming and i have an application with MVVM. I have a StackPanel and inside the StackPanel there is a ItemsControl named ListView and the Itemsource is my ObserveCollection NewProducts which conatins ProductID and Name.
So what is my code does, well it generates buttons with Names from my Collection NewProducts and i wanna give this buttons Command that triggers when i click on them.
I try this:
<StackPanel Grid.Row="1">
<ItemsControl x:Name="ListViewProducts" ItemsSource="{Binding NewProducts}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10" Width="auto" Height="auto">
<StackPanel>
<Border Width="100" Height="40" CornerRadius="5">
<Button Content="{Binding Name}" Tag="{Binding ProductId}" FontWeight="Bold" Command="{BindingSaleViewModel.SendCommand}"/>
</Border>
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
But when i run my program and when i click on my buttons nothing happens. Its because the Command that i have in my SaleViewModel was not found in Product which is my Model class. And i wanna know if i can somehow get the path to my Command in my ViewModel.
Thanks, and please forgive me mine English, I know its bad.
Binding methods in WPF is unfortunately not that simple.
The command is actually a class, that implements the ICommand interface.
You can find an example here: How to Bind a Command in WPF.
Otherwise you can bind the click event to a method in code-behind <Button Click="Button_Click"/>
In code-behind:
private void Button_Click(object sender, RoutedEventArgs e)
{
// Add your code here...
}
Give your root element a name. eg:
<UserControl x:Class="blah"
other stuff
x:Name="root">
Then you can reference the root element in your binding, who's datacontext will (presumably?) be your SaleViewModel:
<Button Content="{Binding Name}" Tag="{Binding ProductId}" FontWeight="Bold"
Command="{Binding DataContext.SendCommand, ElementName=root}"
CommandParameter="{Binding}"/>
Also note the CommandParameter binding which will pass the button's data context (Product) through to your command, otherwise you won't know which product the command relates to.
Related
I'm trying to bind dynamically, in code behind, a VM property (an obseravble collection) to image in listBox that sits inside a usercontrol I show on my window,
but it's not working.
This is the XAML of the user control:
<WrapPanel x:Name="panel" HorizontalAlignment="Center" Focusable="False" FocusManager.IsFocusScope="False">
<ListBox x:Name="MazeListBox" ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid x:Name="MazeUniformGrid" Columns="{Binding VM_MazeCols}" Rows="{Binding VM_MazeRows}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image x:Name="Img" Margin="-6" Source="{Binding}" Focusable="False"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</WrapPanel>
This are the usercontrols in XAML of the Outer Window:
<Controls:MazeBoard1 x:Name="you" Margin="0,0,650,0" Grid.Column="0" Height="Auto" Width="Auto" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Controls:MazeBoard1 x:Name="other" Margin="650,0,0,0" Grid.Column="0" Height="Auto" Width="Auto" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"/>
This is how I bind dynamically in the cs of the window:
Binding youBinding = new Binding
{
Path = new PropertyPath("VM_MazeDisplay"),
};
Binding otherBinding = new Binding
{
Path = new PropertyPath("VM_OtherMazeDisplay"),
};
you.MazeListBox.SetBinding( ContentControl.ContentProperty,youBinding);
other.MazeListBox.SetBinding(ContentControl.ContentProperty, otherBinding);
I'll appreciate your help.
Thank you
Everything looks weird on your XAML...but if you do the following:
you.MazeListBox.SetBinding(ListBox.DataContextProperty, youBinding)
or
you.SetBinding(UserControl.DataContextProperty, youBinding)
or even you.MazeListBox.SetBinding(ListBox.ItemsSourceProperty, youBinding) (you will have to remove the binding into your xaml).
You should have the expected results.
However, why doing a binding in this point and not just only setting the DataContext? Something like you.DataContext = VM_MazeDisplay (assuming the instance of the VM is called that way).
Also, why do you put your ListBox into a WrapPanel?
I have an MVVM app. I want a collection of buttons to be represented from the ViewModel and be dynamic.
Which means I want to populate the window with controls from the ViewModel.
I tried creating a content control and binding it's Content property to a Grid which I will put buttons in. The binding did not work, it remains empty.
I tried binding it to a simple string, still nothing. I should mention that other simple bindings do work, so that's why it's weird.
The creation of the UserControl:
<TabControl HorizontalAlignment="Left" Height="696" Margin="429,0,0,32" VerticalAlignment="Bottom" Width="552" ItemsSource="{Binding TabCollection}">
<TabControl.Resources>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<cattab:CategoryTab/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>`
The binding in the UserControl:
<ContentControl HorizontalAlignment="Left" Height="286" Margin="98,152,0,-396" VerticalAlignment="Top" Width="313">
<ContentControl Content="{Binding Favorites}" Margin="0,30,0,0" VerticalAlignment="Top"/>
</ContentControl>`
MainViewModel:
//**** Initilize TabCollection with fake data (temporary)
TabCollection.Add(new CategoryTabViewModel { Header = "בדיקה11" });
TabCollection.Add(new CategoryTabViewModel { Header = "בדיקה2" });
UserControl ViewModel:
public CategoryTabViewModel()
{
SearchText = "bbbaaaa";
Favorites.Add(new Button());
}
The binding of SearchText works, on Favorites it's not
Try to use an ItemsControl
Here's a good tutorial: http://www.wpf-tutorial.com/list-controls/itemscontrol/
<ItemsControl HorizontalAlignment="Left" Height="286" Margin="98,152,0,-396" VerticalAlignment="Top" Width="313" Content="{Binding Favorites}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use a WrapPanel to manage your layout: http://www.wpftutorial.net/WrapPanel.html
After reading your updates I can say that your code is not MVVM compliant because your ViewModel layer is aware of the View layer (you create Buttons in your ViewModel)
What you need to do:
XAML of your CategoryTab
<ItemsControl ItemsSource="{Binding Favorites, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button/><!--Show whatever you want here-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to create new Button every time an object is added to Favorites you will need to make Favorites an ObservableCollection.
I have a Page and a viewmodel is set as its datacontext. in that page I have a list. which is populating through a property in the viewmodel. List has a user control. and that user control has a button. I want that button to be bind with a command that is in viewmodel. Is there anyway to do that?
<Page DataContext=PageViewModel>
...
<ScrollViewer Grid.Row="3" Margin="20,0" Visibility="{Binding ByVenueSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel>
<ItemsControl ItemsSource="{Binding EventsListByVenue}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<myControls:EventDetails /> <!--in this control i want to bind a command available in PageViewModel-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
...
</Page>
with help of #FunksMaName, I solved this. I am sure there are more elegant and better approach, but yet this is a quick & easy solution for me:
<Button Style="{StaticResource NoHighlightButtonStyle}" Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Visibility="{Binding link,Converter={StaticResource DataAvailabilityToVisibilityConverter}}" Command="{Binding Path=Event.LinkCommand,Source={StaticResource Locator}}" >
<TextBlock Margin="0,5" Foreground="SkyBlue" Text="{Binding link}" TextWrapping="Wrap" FontSize="16"/>
</Button>
things to note:
i think xaml searched the command parameter in context of command only, so it was giving me null parameter, while the same binding was working fine for textblock inside Button. So i tricked it to store the value in tag, and used it from there.
Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}"
<ItemsControl Name="CanvasTableMap" ItemsSource="{Binding}" ItemsPanel="{DynamicResource ItemsPanelTemplate1}" ItemTemplate="{DynamicResource DataTemplate1}">
<ItemsControl.Resources>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<WrapPanel Background="{DynamicResource ContentBackground}" />
</ItemsPanelTemplate>
<DataTemplate x:Key="DataTemplate1">
<Button Canvas.Left="100" Content="{Binding Name}" Template="{DynamicResource ButtonTableTemplate}"></Button>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Here is my code.No problem with that. I have created an adorner and i would like to add an adorner for each button when i want. It is a little difficult as i dont know how to get the Buttons. CanvasTableMap.Items returns the Model so i dont know how to get access to the controls efficiently.
An easy way to do that is to define a handler for the Loaded event of the button, and add the adorner in that handler:
XAML
<Button Canvas.Left="100" Content="{Binding Name}" ... Loaded="Button_Loaded" />
Code-behind
private void Button_Loaded(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var layer = AdornerLayer.GetAdornerLayer(button);
// Add the adorner
...
}
If you don't want to use code-behind, you can create an attached behavior (either with System.Windows.Interactivity or by creating an attached property)
You can use the ItemContainerGenerator to get the control created from the data (ContainerFromItem). Usually doing things that way is not such a good idea though.
I am trying to create a menu bar for my application. I have created a collection in xaml that I will contain menu items that my menu will bind to.
In xaml I have created an array that I use as my static resource for binding.
<coll:ArrayList x:Key="MenuOptionsList">
<model:DashboardMenuBarItem
Icon="the location of an image in my images folder"
DisplayName="The test that will appear under my button"
CommandName="someCommandInMyViewModel"/>
</coll:ArrayList>
I am using a listbox with a data template to show these items as follows.
<ListBox x:Name="lstNavigateTo" MinWidth="400" DockPanel.Dock="Top"
ItemsSource="{Binding Source={StaticResource MenuOptionsList}}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
Style="{StaticResource horizontalListTemplate}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="5">
<Button Height="60" Width="60"
Command="{Binding Mode=OneWay, Path=CommandName}">
<Button.Content>
<Image Source="{Binding Path=Icon}" Grid.Row="0" />
</Button.Content>
</Button>
<TextBlock Text="{Binding Path=DisplayName}"
Width="100" TextAlignment="Center" Grid.Row="1" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My problem is that I am using the MVVM design pattern and cannot get the command bindings to work on the button click. Previously I would have managed the button click like this.
Command="{Binding someCommandInMyViewModel}"
That would work fine but when I try to bind a command to a property of an item in my collection the command will not fire.
Does anyone know how I can achieve this.
The CommandName property in your collection is of type String, whereas the Command property on Button is of type ICommand. In what way are you expecting WPF to resolve an ICommand from a String? You'll need to help it: either create a converter and use it in your binding, or change your CommandName property so that it contains an actual ICommand rather than a String.