I'm a newbie in wpf and i know that this question has been asked other times, and i tried to implement some solutions that i found. But it's not working. I'm doing something wrong but i can't see what it is.
I've created a new simple application to test this problem.
namespace WpfApp3
{
public class MyElement
{
public string Text { get; set; }
public MyElement(string t)
{
Text = t;
}
}
public class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_handler(parameter);
}
private Action<object> _handler;
public MyCommand(Action<object> handler) { _handler = handler; }
}
public class MyItemsControlViewModel
{
ObservableCollection<MyElement> _items;
public ObservableCollection<MyElement> MyElementItems { get { return _items; } set { _items = value; RaisePropertyChanged("MyElementItems"); } }
ObservableCollection<MyElement> _temporayList;
private ICommand _itemClicked;
public ICommand ItemClicked { get { return _itemClicked; } }
public MyItemsControlViewModel()
{
_items = new ObservableCollection<MyElement>();
_temporayList = new ObservableCollection<MyElement>();
_itemClicked = new MyCommand(OnItemSelected);
AddItem("Element 1");
AddItem("Element 2");
AddItem("Element 3");
UpdateList();
}
public void UpdateList()
{
MyElementItems = _temporayList;
}
public void AddItem(string t)
{
MyElement item = new MyElement(t);
_temporayList.Add(item);
}
public void OnItemSelected(object param)
{
Debug.WriteLine("Executed!");
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML
<UserControl x:Class="WpfApp3.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
d:DesignHeight="1080" d:DesignWidth="570"
x:Name="myCustomControl">
<Grid >
<Button x:Name="btnOutsideItemsControl" Width="100" Height="100 " VerticalAlignment="Top" Command="{Binding ItemClicked}" />
<ItemsControl
x:Name="listItems"
ScrollViewer.PanningMode="None"
IsEnabled="False"
Background = "Transparent"
HorizontalAlignment="Center"
ItemsSource="{Binding MyElementItems}" Margin="0,152,0,0" Width="549">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" Margin="50,0,0,0"
Background="Transparent" Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button
Content="{Binding Text}"
Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
The component is used in MainWindow.xaml.
namespace WpfApp3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private MyItemsControlViewModel _myViewModel;
public MyItemsControlViewModel MyViewModel { get { return _myViewModel; } }
public MainWindow()
{
_myViewModel = new MyItemsControlViewModel();
InitializeComponent();
myCustomControl.DataContext = MyViewModel;
}
}
}
XAML
<Window x:Class="WpfApp3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp3"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<local:MyUserControl x:Name="myCustomControl"/>
</Grid>
</Window>
When i run the application i can see correctly the list of 3 items with the correct text.
But if i click on one of the button of the list i can't see the output of Debug.WriteLine("Executed!");
But if i click on the button btnOutsideItemsControl that is outside the ItemsControl, it works. I can see the output of Debug.WriteLine("Executed!");
So i think that also the definition of the command is correct.
To bind correctly the Command property of Button inside the ItemsControl i try this
<Button Command="{Binding ElementName=listItems, Path=DataContext.ItemClicked}">
And also this
<Button Command="{Binding DataContext.ItemClicked, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ItemsControl}}">
But it not works.
Please help!
You're gonna kick yourself once I tell you.
Your problem is that you set IsEnabled="False" on your ItemsControl. Remove it and all will be well with the universe.
Related
My main window is currently displaying the YearView in a content control via DataTemplated YearViewModel. When I click the month button on the YearView I want the main window to instead display the MonthView. The MainViewModel (view model of the main window not shown) retrieves the view model to be displayed from '_navigationStorage.CurrentViewModel' as seen in NavigateMonthCommand.Execute(). But when I click the month button, NavigateMonthCommand.Execute() is never called.
Is the month button binding not working? In YearView.xaml should I be specifying the DataContext differently?
MainWindow.xaml
<Window x:Class="Calandar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="clr-namespace:Calandar.ViewModels"
xmlns:views="clr-namespace:Calandar.Views"
xmlns:local="clr-namespace:Calandar"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="LightSlateGray">
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:YearViewModel}">
<views:YearView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:MonthViewModel}">
<views:MonthView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>
YearView.xaml
<UserControl x:Class="Calandar.Views.YearView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Calandar.Views"
xmlns:viewmodels="clr-namespace:Calandar.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="White" DataContext="viewmodels:YearViewModel">
<StackPanel>
<TextBlock Text="Year" FontSize="55"/>
<Button Content="Month" Command="{Binding NavigateMonthCommand}"/>
</StackPanel>
</Grid>
</UserControl>
YearViewModel.cs
public class YearViewModel : ViewModelBase
{
public ICommand NavigateMonthCommand { get; }
public YearViewModel(NavigationStorage navigationStorage)
{
NavigateMonthCommand = new NavigateMonthCommand(navigationStorage);
}
}
NavigateMonthCommand.cs
public class NavigateMonthCommand : CommandBase
{
private readonly NavigationStorage _navigationStorage;
public NavigateMonthCommand(NavigationStorage navigationStorage)
{
_navigationStorage = navigationStorage;
}
public override void Execute(object parameter)
{
;
_navigationStorage.CurrentViewModel = new MonthViewModel();
}
}
CommandBase.cs
public abstract class CommandBase : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => true;
public abstract void Execute(object parameter);
public void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
Look this one
Without iNotifyPropertyChanged Viewmodel cannot get data from view interface. You must connect properly. You should derive ViewModelBase from INotifyPropertyChanged.
I think you have created your BaseViewModel, you can create the method there.
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
you need to call it while doing property set operation in the YearViewModel you specified. Sample:
private string surName;
public string SurName
{
get { return surName; }
set { surName = value; OnPropertyChanged(nameof(SurName)); }
}
Good Luck
I would like to ask a question regarding the UI update of a WPF application based on changes applied to MVVM objects stored in a ObservableCollection. But first, let me explain my intuition.
I have the following files created to support my Project Solution. In total there are 5 files, so I present their code for you to replicate the issue. Copy-paste the code below in a new Solution project (WPF - .NET Core) and see for yourself my issue.
File 1: App.xaml
<Application x:Class="WpfAppTestingScenarios.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfAppTestingScenarios"
StartupUri="Window1.xaml">
<Application.Resources>
</Application.Resources>
</Application>
File 2: Window1.xaml
<Window x:Class="WpfAppTestingScenarios.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppTestingScenarios"
d:DataContext="{d:DesignInstance Type=local:LoginScreenViewModel}"
mc:Ignorable="d"
Title="Window1"
Height="450"
Width="800">
<Grid>
<Button Content="Click me"
Command="{Binding Path=LoginCommand}"
Height="20"
Width="110"/>
</Grid>
</Window>
File 3: MainWindow.xaml
<Window x:Class="WpfAppTestingScenarios.MainWindow"
x:Name="MainWindowName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppTestingScenarios"
d:DataContext="{d:DesignInstance Type=local:MainWindowViewModel}"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160"/>
<ColumnDefinition Width="640"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="225"/>
<RowDefinition Height="210"/>
</Grid.RowDefinitions>
<StackPanel
x:Name="StackPanel1"
Visibility="{Binding StackPanelVisibility1}"
Grid.Row="0"
Grid.Column="1">
<TextBlock Text="Hello World 1"/>
</StackPanel>
<Button
IsEnabled="{Binding Path=EnableViewButton1, UpdateSourceTrigger=PropertyChanged, FallbackValue=false}"
Content="View"
Width="80"
Height="25"
FontSize="10"
FontWeight="Light"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Grid.Row="0"
Grid.Column="1">
</Button>
<StackPanel
x:Name="StackPanel2"
Visibility="{Binding StackPanelVisibility2}"
Grid.Row="1"
Grid.Column="1">
<TextBlock Text="Hello World 2"/>
</StackPanel>
<Button
IsEnabled="{Binding Path=EnableViewButton2}"
Content="View"
Width="80"
Height="25"
FontSize="10"
FontWeight="Light"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Grid.Row="1"
Grid.Column="1">
</Button>
</Grid>
</Window>
File 4: Window1.xaml.cs
using Prism.Commands;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace WpfAppTestingScenarios
{
public class LoginScreenViewModel : INotifyPropertyChanged
{
public ICommand LoginCommand
{
get { return new DelegateCommand<object>(FuncLoginCommand); }
}
public void FuncLoginCommand(object parameters)
{
MainWindow WindMain = new MainWindow();
WindMain.Show();
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new LoginScreenViewModel();
}
}
}
File 5: MainWindow.xaml.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace WpfAppTestingScenarios
{
public class MyCustomClass : INotifyPropertyChanged
{
public string Key { get; set; }
private object _value;
public object Value
{
get { return _value; }
//set { _value = value; NotifyPropertyChanged($"{Value}"); }
//change to
set { _value = value; NotifyPropertyChanged(nameof(Value)); } //still no luck
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
//1. - StackPanelVisibility 1
private Visibility _StackPanelVisibility1;
public Visibility StackPanelVisibility1
{
get
{
return _StackPanelVisibility1;
}
set
{
_StackPanelVisibility1 = value;
OnPropertyChanged("StackPanelVisibility1");
}
}
//2. - StackPanelVisibility 2
private Visibility _StackPanelVisibility2;
public Visibility StackPanelVisibility2
{
get
{
return _StackPanelVisibility2;
}
set
{
_StackPanelVisibility2 = value;
OnPropertyChanged("StackPanelVisibility2");
}
}
//3. - EnableViewButoon 1
private bool _EnableViewButton1;
public bool EnableViewButton1
{
get
{
return _EnableViewButton1;
}
set
{
_EnableViewButton1 = value;
OnPropertyChanged("EnableViewButton1");
}
}
//4. - EnableViewButoon 2
private bool _EnableViewButton2;
public bool EnableViewButton2
{
get
{
return _EnableViewButton2;
}
set
{
_EnableViewButton2 = value;
OnPropertyChanged("EnableViewButton2");
}
}
private void CustomFunction(ObservableCollection<MyCustomClass> UICollection)
{
if ((Visibility)UICollection[0].Value == Visibility.Hidden)
UICollection[0].Value = Visibility.Visible;
if ((bool)UICollection[1].Value == false)
UICollection[1].Value = true;
}
public MainWindowViewModel()
{
ObservableCollection<MyCustomClass> dict = new ObservableCollection<MyCustomClass>
{
new MyCustomClass { Key = "StackPanelVisibility", Value = StackPanelVisibility1 },
new MyCustomClass { Key = "EnableViewButton", Value = EnableViewButton1 }
};
CustomFunction(dict);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
}
So even though everything was set successfully and with no errors, when I run the application and I click the button Click me the UI and thus the objects of the MainWindow are not updated.
Initially, I tried this logic with Dictionaries. But then I read that Dictionary cannot update the UI of a WPF application so I changed it to an ObservableCollection. However, both approaches didn't work for me.
Edit
Based on this answer, I created the following code
public class MyCustomClass : INotifyPropertyChanged
{
public string Key { get; set; }
private object _value;
public object Value
{
get { return _value; }
//set { _value = value; NotifyPropertyChanged($"{Value}"); }
//change to
set { _value = value; NotifyPropertyChanged(nameof(Value)); } //still no luck
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
But still, I don't observe any UI change.
My end result would be to enable the two buttons in the MainWindow like in the screen below (when I click the button Click me)
Image of my desired result
I have a main control (MainWindow.xaml) and an user control (ItemView.xaml). MainWindow contains an ItemsControl for all the ItemView-s and a simple button to add an item. All logic is (should be?) inside two corresponding viewmodels (MainWindowViewModel and ItemViewModel). Below is my code (made it as short as possible), but I have two problems with it:
When a new item is added it is correctly displayed but the exception is raised (Cannot create default converter to perform 'two-way' conversions between types 'WpfApplication1.ItemView' and 'WpfApplication1.ItemViewModel'.).
The OnDelete event handler in MainWindowViewModel is never raised? Edit: actually the ViewModel property inside BtnDeleteClick is null so yeah... of course.
Btw - I use Fody PropertyChanged.
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Width="100" Height="35" Content="Add" HorizontalAlignment="Left" Margin="10" Click="BtnAddClick"></Button>
<Border Grid.Row="1" MinHeight="50">
<ItemsControl ItemsSource="{Binding ViewModel.Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<wpfApplication1:ItemView ViewModel="{Binding ., PresentationTraceSources.TraceLevel=High, Mode=TwoWay}"></wpfApplication1:ItemView>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</Grid>
</Window>
MainWindow.xaml.cs:
[ImplementPropertyChanged]
public partial class MainWindow
{
public MainWindowViewModel ViewModel { get; set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new MainWindowViewModel();
}
private void BtnAddClick(object sender, RoutedEventArgs e)
{
ViewModel.Add();
}
}
MainWindowViewModel.cs:
[ImplementPropertyChanged]
public class MainWindowViewModel
{
public ObservableCollection<ItemViewModel> Items { get; set; }
public MainWindowViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
}
public void Add()
{
var item = new ItemViewModel();
item.OnDelete += (sender, args) =>
{
Debug.WriteLine("-- WAITING FOR THIS TO HAPPEN --");
Items.Remove(item);
};
Items.Add(item);
}
}
ItemViewModel.xaml:
<UserControl x:Class="WpfApplication1.ItemView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<Button Width="100" Height="35" Content="Delete" Click="BtnDeleteClick"></Button>
</Grid>
</UserControl>
ItemView.xaml.cs:
[ImplementPropertyChanged]
public partial class ItemView
{
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register
(
"ViewModel", typeof(ItemViewModel), typeof(ItemView), new UIPropertyMetadata(null)
);
public ItemViewModel ViewModel
{
get { return (ItemViewModel)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public ItemView()
{
InitializeComponent();
}
private void BtnDeleteClick(object sender, RoutedEventArgs e)
{
ViewModel.Delete();
}
}
And ItemViewModel.cs:
[ImplementPropertyChanged]
public class ItemViewModel
{
public event EventHandler OnDelete;
public void Delete()
{
var handler = OnDelete;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
You should not set
DataContext="{Binding RelativeSource={RelativeSource Self}}"
in the XAML of your ItemView. It effectively breaks the ViewModel="{Binding .}" binding in MainWindow.xaml, because the DataContext is no longer an ItemsViewModel, but an ItemsView.
As a rule, you should never explicitly set the DataContext of a UserControl, because all "external" bindings would then require an explicit Source or RelativeSource value.
That said, you're doing all this way too complicated. Instead of having a button click handler in your ItemsView, you could simply have a view model with a delete command, and bind the Button's Command property to this command.
It may look like this:
public class ItemViewModel
{
public string Name { get; set; }
public ICommand Delete { get; set; }
}
public class MainViewModel
{
public MainViewModel()
{
Items = new ObservableCollection<ItemViewModel>();
}
public ObservableCollection<ItemViewModel> Items { get; private set; }
public void AddItem(string name)
{
Items.Add(new ItemViewModel
{
Name = name,
Delete = new DelegateCommand(p => Items.Remove(p as ItemViewModel))
});
}
}
and would be used like this:
<UserControl x:Class="WpfApplication1.ItemView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Button Content="Delete"
Command="{Binding Delete}"
CommandParameter="{Binding}"/>
</Grid>
</UserControl>
How do I get my custom item collection to show up in my list view using WPF data bindings?
I have a tried to make a ViewModel and a custom collection that the ViewModel manipulates, in an attempt to get this collection to show up in a listview. I have a view model and a custom collection and a custom item class:
public class TranslationViewModel
{
public TranslationViewModel() { this.translatedItems = new TransListboxCollection(); }
public TransListboxCollection translatedItems { get; private set; }
public void addTranslatedItem(TransListboxItem message)
{
translatedItems.Add(message);
}
}
public class TransListboxCollection : BindingList<TransListboxItem>
{
public TransListboxCollection()
{
//initialize
}
}
public class TransListboxItem : INotifyPropertyChanged
{
private String _rawString;
private String _tString;
public String rawString
{
get { return _rawString; }
set { _rawString = value; NotifyPropertyChanged("rawString"); }
}
public String tString
{
get { return _tString; }
set { _tString = value; NotifyPropertyChanged("tString"); }
}
public TransListboxItem(String value)
{
this.tString = value;
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return this.tString;
}
}
I have a WPF element hosted in a windows form
public partial class wGlobal : UserControl
{
public TranslationViewModel tvm { get; set; }
public wGlobal()
{
InitializeComponent();
this.DataContext = tvm;
}
}
The XAML code for such
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MHF_Transcoder_3" x:Class="MHF_Transcoder_3.wGlobal"
mc:Ignorable="d"
d:DesignWidth="1000" d:DesignHeight="150">
<Grid Width="1000" Height="150">
<ListView x:Name="listView1" ItemsSource="{Binding tvm}" HorizontalAlignment="Left" Width="1000" Height="150" VerticalAlignment="Top" Background="Black" Foreground="White" RenderTransformOrigin="0.5,0.5">
<DataTemplate>
<TextBlock Text="{Binding tString}" ToolTipService.ToolTip="{Binding rawString}" />
</DataTemplate>
</ListView>
</Grid>
and I have that element hosted in a windows form control
public partial class frmGlobal : Form
{
wGlobal xamlForm;
TranslationViewModel tvm;
public frmGlobal()
{
InitializeComponent();
tvm = new TranslationViewModel();
xamlForm = (wGlobal)elementHost1.Child;
xamlForm.tvm = tvm;
}
delegate void addMessageCallback(TransListboxItem message);
public void addMessage(TransListboxItem message) {
tvm.addTranslatedItem(message);
}
}
When I get the program up and launch everything, all my list view says is "System.Windows.DataTemplate". I've never really worked with WPF or data bindings before. I'm open to any and all advice and suggestions. Please help me get this setup and properly working.
Wrap Datatemplate with ItemTemplate
<ListView x:Name="listView1" ItemsSource="{Binding translatedItems}" HorizontalAlignment="Left" Width="1000" Height="150" VerticalAlignment="Top" Background="Black" Foreground="White" RenderTransformOrigin="0.5,0.5">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding tString}" ToolTipService.ToolTip="{Binding rawString}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Also as the tvm is the datacontext, you must bind to the collection "translatedItems"
I have a grid in WPF that contains a button which should make a user control visible. How do I make this possible using MVVM pattern and /or code behind?
In your view model you want a bool property for the visibility of the user control. We'll call it IsUserControlVisible. Now you'll need a command in your view model that will set the IsUserControlVisible property to true. We'll call this ShowUserControlCommand.
In XAML you would bind the visibility of the User Control to IsUserControlVisible. In WPF there is a BooleanToVisibilityConverter, so we don't have to create our own converter. Your XAML would look something like this.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Command="{Binding ShowUserControlCommand}">Show</Button>
<UserControl Grid.Row="1" Visibility="{Binding IsUserControlVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</Window>
I hope this helps.
Following a full example on how you can achieve this in MVVM with an illustration of ICommand interface.
your main should look like this
XAML:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350"
Width="525"
xmlns:my="clr-namespace:WpfApplication3">
<Grid>
<my:UserControl1 Background="Aqua"
Visibility="{Binding ChangeControlVisibility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left"
Margin="111,66,0,0"
x:Name="userControl11"
VerticalAlignment="Top"
Height="156"
Width="195" />
<Button Content="Button"
Height="36"
HorizontalAlignment="Left"
Margin="36,18,0,0"
Name="button1"
VerticalAlignment="Top"
Width="53"
Command="{Binding MyButtonClickCommand}" />
</Grid>
</Window>
MainWindow.cs
using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace WpfApplication3
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
}
ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public MyViewModel()
{
_myCommand = new MyCommand(FuncToCall,FuncToEvaluate);
}
private ICommand _myCommand;
public ICommand MyButtonClickCommand
{
get { return _myCommand; }
set { _myCommand = value; }
}
private void FuncToCall(object context)
{
//this is called when the button is clicked
//for example
if (this.ChangeControlVisibility== Visibility.Collapsed)
{
this.ChangeControlVisibility = Visibility.Visible;
}
else
{
this.ChangeControlVisibility = Visibility.Collapsed;
}
}
private bool FuncToEvaluate(object context)
{
return true;
}
private Visibility _visibility = Visibility.Visible;
public Visibility ChangeControlVisibility
{
get { return _visibility; }
set {
_visibility = value;
this.OnPropertyChanged("ChangeControlVisibility");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Command:
class MyCommand : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public MyCommand(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
Generally speaking you have a boolean flag in your viewmodel that is bound to the user controls Visibility using an appropriate converter. You have a command in your viewmodel that is bound to the button's Command property. The Execute method of the command toggles the boolean flag.
Edit:
If you only need the button to make something visible on form, consider the Expander control that already does this out of the box.
<Expander>
<YourUserControl/>
</Expander>