I'm trying to build a DTO to store the software configuration, but I'm stuck because my view is not sending the data to my ViewModel and also to my DTO.
I need to transfer 2 textbox and 3 combobox to my DTO, but using this code the values are always empty.
My ViewModel:
public class ViewModelProcessamentoArquivo : ViewModelBase
{
private PesquisaConfiguracao pesquisaConfiguracao;
public PesquisaConfiguracao PesquisaConfiguracao
{
get { return pesquisaConfiguracao; }
set
{
pesquisaConfiguracao = value;
base.OnPropertyChanged("PesquisaConfiguracao");
}
}
}
My DTO/Model
public class PesquisaConfiguracao
{
public string ArquivoOrigem { get; set; }
public string ArquivoDestino { get; set; }
public string TipoPesquisa { get; set; }
public string PesquisaVeicular { get; set; }
public string PesquisaCrediticia { get; set; }
}
And my View is like this.
<TextBox Name="txtBuscarArquivoOrigem" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Height="30" Margin="10, 0" Text="{Binding PesquisaConfiguracao.ArquivoOrigem}" />
<TextBox x:Name="txtBuscarArquivoDestino" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" Height="30" Margin="10, 0" Text="{Binding PesquisaConfiguracao.ArquivoDestino}" IsEnabled="false" />
...
Do you guys know why it's happening? I've used something similar in my other project and worked just fine. Also if you have any other possibly way to fix this issue, please comment!
First UpdateSourceTrigger PropertyChanged, that way the target (view) will update your source object on every change:
<TextBox Name="txtBuscarArquivoOrigem" Height="30" Text="{Binding PesquisaConfiguracao.ArquivoOrigem, UpdateSourceTrigger=PropertyChanged}" />
Then implement in your source object the INotifyPropertyChange Interface on it's properties in order to update the view when the value has changed:
private string _arquivoOrigem;
public string ArquivoOrigem
{
get
{
return _arquivoOrigem;
}
set
{
_arquivoOrigem = value;
OnPropertyChanged("ArquivoOrigem");
}
}
Put a BreakPoint in the property setter and it will break there when you change the value in the view TextBox.
If it doesn't work for you probably forgot to set your DataContext to your ViewModel:
<Window x:Class="WpfApplication1.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"
mc:Ignorable="d"
Title="MainWindow"
DataContext="{StaticResource MainViewModel}">
Or did not initialize your source object:
public MainViewModel()
{
pesquisaConfiguracao = new PesquisaConfiguracao
{
ArquivoDestino = "aaa",
ArquivoOrigem = "bbb",
PesquisaCrediticia = "ccc",
PesquisaVeicular = "dddd",
TipoPesquisa = "eee"
};
}
Related
I'm developing a WPF application using caliburn.micro MVVM framework..
In-order to develop a search screen, I need to dynamically load fields into the view, based on model properties.
Consider below view and view model:
SearchViewModel
SearchView
Let's assume T is a type of Product in below example.
public class SearchViewModel<T>
{
public T Item{get;set;}
}
public class Product
{
public int Id{get;set;}
public string Name{get;set;}
public string Description{get;set;}
}
I have a user control called SearchView.xaml with no contents on it.
Whenever View is loaded new fields should be added to the view and field should be bound to the properties.
According to above code example, there are 3 public properties in the Product class, therefore 3 TextBoxes should be added to the view dynamically. When user enters data in the text field, corresponding property should be updated.
Is this possible?
Can any experts help me to achieve this by providing some examples?
I would propose going about this differently. Instead of thinking about dynamically adding properties to a view / model, I would think about adding information about those properties to a list on the viewmodel. That list would then be bound to an ItemsControl with a template that looks like a TextBox.
So your view-model would have a property on it for the "thing" you want to examine. In the setter for this property, use reflection to enumerate the properties you are interested in, and add an instance of some kind of FieldInfo class (that you create) to the list of properties with the binding.
This has the benefit of keeping everything all MVVM compatible too, and there is no need to dynamically create controls with your own code.
The example below uses my own MVVM library (as a nuget package) rather than caliburn.micro, but it should be similar enough to follow the basic idea. The full source code of the example can be downloaded from this BitBucket repo.
As you can see in the included screenshots, the search fields are created dynamically on the view without any code in the view. Everything is done on the viewmodel. This also gives you easy access to the data that the user enters.
The view-model:
namespace DynamicViewExample
{
class MainWindowVm : ViewModel
{
public MainWindowVm()
{
Fields = new ObservableCollection<SearchFieldInfo>();
SearchableTypes = new ObservableCollection<Type>()
{
typeof(Models.User),
typeof(Models.Widget)
};
SearchType = SearchableTypes.First();
}
public ObservableCollection<Type> SearchableTypes { get; }
public ObservableCollection<SearchFieldInfo> Fields { get; }
private Type _searchType;
public Type SearchType
{
get { return _searchType; }
set
{
_searchType = value;
Fields.Clear();
foreach (PropertyInfo prop in _searchType.GetProperties())
{
var searchField = new SearchFieldInfo(prop.Name);
Fields.Add(searchField);
}
}
}
private ICommand _searchCommand;
public ICommand SearchCommand
{
get { return _searchCommand ?? (_searchCommand = new SimpleCommand((obj) =>
{
WindowManager.ShowMessage(String.Join(", ", Fields.Select(f => $"{f.Name}: {f.Value}")));
})); }
}
}
}
The SearchFieldInfo class:
namespace DynamicViewExample
{
public class SearchFieldInfo
{
public SearchFieldInfo(string name)
{
Name = name;
}
public string Name { get; }
public string Value { get; set; } = "";
}
}
The view:
<Window
x:Class="DynamicViewExample.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:local="clr-namespace:DynamicViewExample"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="525"
Height="350"
d:DataContext="{d:DesignInstance local:MainWindowVm}"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ComboBox
Grid.Row="0"
ItemsSource="{Binding Path=SearchableTypes}"
SelectedItem="{Binding Path=SearchType}" />
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" />
<TextBox Width="300" Text="{Binding Path=Value}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Grid.Row="2" Command="{Binding Path=SearchCommand}">Search</Button>
</Grid>
</Window>
The model classes:
class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string PhoneNumber { get; set; }
public string Id { get; set; }
}
class Widget
{
public string ModelNumber { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
Here is a basic example of how you could generate a TextBox per public property of the T in the control using reflection.
SearchView.xaml:
<Window x:Class="WpfApplication4.SearchView"
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:WpfApplication4"
mc:Ignorable="d"
Title="SearchView" Height="300" Width="300">
<StackPanel x:Name="rootPanel">
</StackPanel>
</Window>
SearchView.xaml.cs:
public partial class SearchView : UserControl
{
public SearchView()
{
InitializeComponent();
DataContextChanged += SearchView_DataContextChanged;
DataContext = new SearchViewModel<Product>();
}
private void SearchView_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue != null)
{
Type genericType = e.NewValue.GetType();
//check the DataContext was set to a SearchViewModel<T>
if (genericType.GetGenericTypeDefinition() == typeof(SearchViewModel<>))
{
//...and create a TextBox for each property of the type T
Type type = genericType.GetGenericArguments()[0];
var properties = type.GetProperties();
foreach(var property in properties)
{
TextBox textBox = new TextBox();
Binding binding = new Binding(property.Name);
if (!property.CanWrite)
binding.Mode = BindingMode.OneWay;
textBox.SetBinding(TextBox.TextProperty, binding);
rootPanel.Children.Add(textBox);
}
}
}
}
}
The other option will obviously be to create a "static" view for each type of T and define the TextBox elements in the XAML markup as usual.
I cant bind the variable from code behind in my wpf radiobutton
Can anyone help me to display the values from the variables in the content from the radio button.
MainWindow.xaml:
<RadioButton GroupName="Preis" Grid.Row="10" Content="{Binding Name1}" FlowDirection="RightToLeft" HorizontalAlignment="Left"/>
<RadioButton GroupName="Preis" Grid.Row="11" Content="{Binding Name2}" FlowDirection="RightToLeft" HorizontalAlignment="Left"/>
<RadioButton GroupName="Preis" Grid.Row="12" Content="{Binding Name3}" FlowDirection="RightToLeft" HorizontalAlignment="Left"/>
<RadioButton GroupName="Preis" Grid.Row="13" Content="{Binding Name4}" FlowDirection="RightToLeft" HorizontalAlignment="Left"/>
<RadioButton GroupName="Preis" Grid.Row="14" Content="{Binding Name5}" FlowDirection="RightToLeft" HorizontalAlignment="Left"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Produkte produkte = new Produkte();
produkte.Name1 = "Handstaubsauger";
produkte.Name2 = "Fensterwascher";
produkte.Name3 = "Dampfreiniger";
produkte.Name4 = "Hochdruckreiniger";
produkte.Name5 = "Geschenkgutschein";
// Regex für Email
String regexEmail = #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
// Hier weitermachen
}
}
Produkte.cs
public class Produkte
{
public String Name1 { get; set; }
public String Name2 { get; set; }
public String Name3 { get; set; }
public String Name4 { get; set; }
public String Name5 { get; set; }
public Int16 Stimmen1;
public Int16 Stimmen2;
public Int16 Stimmen3;
public Int16 Stimmen4;
public Int16 Stimmen5;
}
public MainWindow()
{
InitializeComponent();
Produkte produkte = new Produkte();
produkte.Name1 = "Handstaubsauger";
produkte.Name2 = "Fensterwascher";
produkte.Name3 = "Dampfreiniger";
produkte.Name4 = "Hochdruckreiniger";
produkte.Name5 = "Geschenkgutschein";
// ADD THIS
DataContext = produkte;
// Regex für Email
String regexEmail = #"^(?("")("".+?(?<!\\)""#)|(([0-9a-z]((\.(?!\.))|[-!#\$%&'\*\+/=\?\^`\{\}\|~\w])*)(?<=[0-9a-z])#))(?(\[)(\[(\d{1,3}\.){3}\d{1,3}\])|(([0-9a-z][-\w]*[0-9a-z]*\.)+[a-z0-9][\-a-z0-9]{0,22}[a-z0-9]))$";
// Hier weitermachen
}
Note that if you try to bind to Stimmen1, Stimmen2, etc. in your view, it will fail, because they are fields. They must have { get; } to be able to bind to them.
You really need to turn Produkte into a proper viewmodel with INotifyPropertyChanged etc., but this will get a read-only view working right now.
In your xaml within the Window tag on the top, you would need to define your DataContext. If it is your code behind it would be Self. Once you have the DataContext set, you will be able to access public properties for binding.
<Window x:Class="Account.Client.PotentialMisallocation.Controls.DisplayAndFilter"
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"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
You will need to expose a public property of type Product in your code behind:
public Product Product
{
get { return new Product() {Id = 1, Name = "James"}; }
}
Then in xaml, you would do sth like:
<Label x:Name="label" Content="{Binding Product.Id}" />
<Label x:Name="label1" Content="{Binding Product.Name}" />
Im trying to implement a simple create-function in my win 8 app. Im trying to use the MVVM-pattern. Im trying to pass a class into my view with my view.model and then simply have a couple of textboxes that lets me create a new object. Here is the ViewModel and class:
public class CreateViewModel : ViewModelBase
{
public Place Place { get; set; }
}
public class Place
{
[PrimaryKey, AutoIncrement]
public int PlaceId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
In an MVC-application i would have done some #Html.TextBoxFor and created a post-method.
In XAML I am not sure of how to do this.
The viewmodel gets passed in to view as it should. I can acess its properties like this:
<TextBox Grid.Row="0" Text="{Binding Path=Place.Title}"/>
<TextBox Grid.Row="0" Text="{Binding Path=Place.Description}"/>
But i do not understand how I can "post" new values back to the ViewModel and create a new object?
EDIT:
From what I can see this is a way to have commands in my ViewModel:
public class CreateViewModel : ViewModelBase
{
public RelayCommand CreatePlaceCommand
{
get;
private set;
}
public Place Place { get; set; }
public CreateViewModel()
{
InitializeCommands();
}
private void InitializeCommands()
{
CreatePlaceCommand =
new RelayCommand(() =>
{
//What goes here?
});
}
}
I also added this code to my XAML:
<TextBox Grid.Row="0" Text="{Binding Place.Title,Mode=TwoWay}"/>
<TextBox Grid.Row="0" Text="{Binding Place.Description,Mode=TwoWay}"/>
<Button Grid.Row="0" Content="Click"
Command="{Binding CreatePlaceCommand}" >
</Button>
Am I on the right track here? Its pretty confusing =)
Here, study this simple example to get hold of MVVM/DataBinding/Commands. It's really simplistic but it should show the "patterns" to use. There's plenty of libs (like MVVMLight) to make commanding etc. simpler and more powerful.
So assuming we have Place entity
public class Place
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public override string ToString()
{
return string.Format("Id={0},Title={1},Description={2}",
Id, Title, Description);
}
}
And you have MainWindow.xaml in your application named wpfApplication1
<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="116"
Width="250">
<!-- set datacontext to mainviewmodel -->
<Window.DataContext>
<wpfApplication1:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<!-- input textboxes for title and description -->
<StackPanel Grid.Row="0">
<TextBox Text="{Binding Place.Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="25" />
<TextBox Text="{Binding Place.Description, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="25" />
</StackPanel>
<!-- button bound to save command, declared in viewmodel -->
<Button Grid.Row="1" Content="Save" Command="{Binding SaveCommand}" />
</Grid>
</Window>
Related MainWindows.xaml.cs contains nothing but InitializeComponents().
Now your MainViewModel, "taking care of all things", could look like
public class MainViewModel
{
private Place _place;
public MainViewModel()
{
// create and register new save command
SaveCommand = new SaveCommand(this);
CommandManager.RegisterClassCommandBinding(
typeof(MainViewModel), new CommandBinding(SaveCommand));
}
// property to hold place data, exposed in UI
public Place Place
{
get { return _place ?? (_place = new Place()); }
set { _place = value; }
}
public ICommand SaveCommand { get; private set; }
}
And simplistic save command implementation used in viewmodel
public class SaveCommand : ICommand
{
public event EventHandler CanExecuteChanged;
private readonly MainViewModel _context;
public SaveCommand(MainViewModel context)
{
_context = context;
}
public void Execute(object parameter)
{
Console.WriteLine(string.Format("Do something with {0}", _context.Place));
}
public bool CanExecute(object parameter)
{
return true;
}
}
Now, this would give you UI, something like below (this example is not type of Store app)
And clicking a button would then spit out
Do something with Id=0,Title=Title,Description=and teh description
I was trying to get it working for few days.
What is wrong in this code?
This is my window XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Rapideo_Client"
x:Class="Rapideo_Client.MainWindow"
Title="NVM" SnapsToDevicePixels="True" Height="400" Width="625">
<Window.Resources>
<DataTemplate x:Key="linksTemplate" DataType="DownloadLink">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=Name}" FontWeight="Bold"></TextBlock>
<Label Content="{Binding Path=SizeInMB}"/>
<Label Content="{Binding Path=Url}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
x:Name="MainListBox"
ItemTemplate="{DynamicResource linksTemplate}">
</ListView>
</Window>
This is my class:
class Rapideo
{
(...)
public List<DownloadLink> Links { get; private set; }
(...)
}
This is my item:
class DownloadLink
{
public string Name { get; private set; }
public string Url { get; private set; }
public DateTime ExpiryDate { get; private set; }
public float SizeInMB { get; private set; }
public int Path { get; private set; }
public string Value { get; private set; }
public LinkState State { get; set; }
public enum LinkState
{
Ready, Downloading, Prepering, Downloaded
}
public DownloadLink(string name, string url, DateTime expiryDate, float sizeInMB, int path, string value, LinkState state)
{
Name = name;
Url = url;
ExpiryDate = expiryDate;
SizeInMB = sizeInMB;
Path = path;
Value = value;
State = state;
}
}
This is my binding:
RapideoAccount = new Rapideo();
MainListBox.ItemsSource = RapideoAccount.Links;
Later in the code I populate that list in RapideoAccount.Links.
But nothing is showing in ListView.
List View is always empty.
Where is mistake in that code?
Yes, it should be an ObservableCollection<DownloadLink> if you're planning on adding to it AFTER you have setup the ItemsSource. If the list is preloaded and you won't be changing it, List<T> would have worked.
Now I do think that
MainListBox.ItemsSource = RapideoAccount.Links;
is still technically a binding. But what you are probably thinking of is binding via the DataContext rather than directly (al la MVVM style). So that'd be:
RapideoAccount = new Rapideo();
this.DataContext = RapideoAccount;
Then in your window, you'd bind your ItemSource like this:
<Window
...
<ListView ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
x:Name="MainListBox"
ItemsSource="{Binding Links}"
ItemTemplate="{DynamicResource linksTemplate}">
</ListView>
</Window>
First off, you should use an ObservableCollection<DownloadLink> rather than a List<DownloadLink> if you're planning on making changes to the list after setting up the binding.
Second of all, just to be clear:
MainListBox.ItemsSource = RapideoAccount.Links;
is not a binding. You're just setting the property. That will work for certain scenarios, but its not really a binding like we normally talk about in WPF.
I think that Links needs to be an ObservableCollection, not a List.
I've got some bindings in UI:
<Window x:Class="Tester.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="377" Width="562" xmlns:my="clr-namespace:MyApp">
<Grid>
<TextBlock Text="{Binding Path=current.Text}" Name="Text1" />
<TextBlock Text="{Binding Path=current.o.Text}" Name="Text2" />
</Grid>
</Window>
Code:
class Coordinator : INotifyPropertyChanged{
List<Myclass1> list;
int currId = 0;
public Myclass1 current{
return list[currId];
}
public int CurrId
{
get { return currId; }
set
{
currId = value;
this.PropertyChanged(this,new PropertyChangedEventArgs("current"));
}
}
class Myclass1{
public string Text{get;}
public Myclass2 o{get;}
}
class Myclass2{
public string Text{get;}
}
When currId changes Tex1 in UI changes too,but Text2 doesn't.
I'm assuming this happens because Text2's source isn't updated.
Does anyone know how to fix it?
I'm not sure if this is what you are looking for, but for something similar I used the PropertyObserver class from Josh Smith's MVVM framework.
It should work assuming you implement your classes properly. With some fixes my code looks like this:
<Window x:Class="WpfBindingTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<StackPanel>
<TextBlock Text="Enter 0, 1 or 2 below and press Tab:"/>
<TextBox Text="{Binding CurrId}"/>
<Button/> <!--Button here is just for another tab stop-->
<TextBlock Text="{Binding Path=current.Text}" Name="Text1" />
<TextBlock Text="{Binding Path=current.o.Text}" Name="Text2" />
</StackPanel>
</Grid>
</Window>
it works fine with classes implemented like this:
public partial class Window1: Window {
public Window1() {
InitializeComponent();
DataContext = new Coordinator();
}
}
class Coordinator: INotifyPropertyChanged {
List<Myclass1> list;
int currId = 0;
public Myclass1 current {
get { return list[currId]; }
}
public int CurrId {
get { return currId; }
set {
currId = value;
this.PropertyChanged(this, new PropertyChangedEventArgs("current"));
}
}
public Coordinator() {
list = new List<Myclass1>(){
new Myclass1(){ Text = "1", o = new Myclass2(){Text="1.1"}},
new Myclass1(){ Text = "2", o = new Myclass2(){Text="2.2"}},
new Myclass1(){ Text = "3", o = new Myclass2(){Text="3.3"}}
};
}
public event PropertyChangedEventHandler PropertyChanged;
}
class Myclass1 {
public string Text { get; set; }
public Myclass2 o { get; set; }
}
class Myclass2 {
public string Text { get; set; }
}
Maybe you need to implement INotifyPropertyChanged in Myclass2 also.