Databinding for Instances of class objects - c#

To preface this I am new to WPF, XAML and C#
I have been looking for that last two days now for a method to create a custom class object that will expose some properties. I then want to bind some text boxes in the UI to those properties but I want to be able to do this for multiple instances of a class (Analog in my case). I am sure this is something simple but I am not understanding how to approach this.
Example of the class:
class Analog
{
public string LblA0
{ get; set; }
}
EDIT:
I have figured out how to bind to a class but I am unsure how I would create another class instance to bind to separately.
<Grid>
<Grid.DataContext>
<local:Analog/>
</Grid.DataContext>
<TextBlock x:Name="labelAnalog0"
Text="{Binding LblA0, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
EDIT 2: ViewModel class?
class ViewModel
{
public Analog Analog0
{ get; set; }
}

Okay thanks to Clemens I think I understand how to solve this.
Here are the classes I have made:
class Analog
{
public Analog(string s)
{
lblA = s;
}
private string lblA;
public string LblA
{
get
{
return lblA;
}
set
{
lblA = value;
}
}
}
class ViewModel
{
Analog analog0 = new Analog("test");
Analog analog1 = new Analog("test2");
public Analog Analog0
{
get
{
return analog0;
}
}
public Analog Analog1
{
get
{
return analog1;
}
}
}
XAML
<TextBlock x:Name="labelAnalog0"
Text="{Binding Analog0.LblA, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock x:Name="labelAnalog1"
Text="{Binding Analog1.LblA, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center"/>
The call to change the datacontext in the mainpage
analogGrid.DataContext = new ViewModel();

Related

Changing the ViewModel's property from another ViewModel

I have the ListBox on my MainView.xaml, selecting the Item forces the ContentControl to display different UserControls. I use Caliburn.Micro library in this propgram. Here's some code:
<ListBox Grid.Row="1" Grid.Column="1" x:Name="ItemsListBox" SelectedItem="0" ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding TextBlock1Text}" x:Name="TextBlock1"/>
<ContentControl Grid.Row="3" Grid.Column="1" Content="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content}" />
The MainViewModel.cs:
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
NotifyOfPropertyChange(() => Name);
}
}
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public MainViewModel()
{
TextBlock1Text = "Test";
Items = new ObservableCollection<ItemsModel>()
{
new ItemsModel { Name="Useless", Content=null },
new ItemsModel { Name="TextChangerViewModel", Content=new TextChangerViewModel(TextBlock1Text) }
};
}
public ObservableCollection<ItemsModel> Items { get; set; }
The ItemsModel.cs:
public class ItemsModel
{
public string Name { get; set; }
public object Content { get; set; }
}
And finally the TextChangerViewModel.cs:
public class TextChangerViewModel : Conductor<object>
{
private string _textBlock1Text;
public string TextBlock1Text
{
get => _textBlock1Text;
set
{
_textBlock1Text = value;
NotifyOfPropertyChange(() => TextBlock1Text);
}
}
public TextChangerViewModel(string textBlock1Text) //passing parameter from another ViewModel
{
TextBlock1Text = textBlock1Text;
}
}
So, the main question is how to change the TextBlock1Text (and the Text value of TextBlock in .xaml as well) in the MainViewModel.cs from the TextChangerViewModel.cs? I was thinking about using something like NotifyCollectionChanged on my Items ObservableCollection, but it work with collection of ItemsModel, not with the VM's, so I'm stuck here.
I'm also not sure if having public object Content { get; set; } in ItemsModel.cs is a good thing if I'm targeting the MVVM pattern, but I don't know the other way to do it (I'm very new to MVVM).
UPD
I'm looking for the property-changing way because I need to change the TextBlock1Text Text from another UserControl. Suppose I have the button on my TextChangerView.xaml: <Button Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Content="Change da text" cal:Message.Attach="ChangeTextButton"/>
And after the click on it I want the text on the parental MainView.xaml to change. But the thing is, I don't know how to change properties in this case, as I wrote above why.
Change the the binding of textblox1 to reference the selected item.
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Name}" x:Name="TextBlock1"/>
or
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding ElementName=ItemsListBox, Path=SelectedItem.Content.TextBlock1Text}" x:Name="TextBlock1"/>

How to relate two binding in wpf in c#

I have a solution in C# with two proyects. The first proyect calls to the second and uses a value of its binding (EstadoVisibleTeclado).
But when I want to use this value whitin a label, It does not appeare.
For example, in the first proyect:
<Label Grid.Column="0" Visibility="{Binding EstadoVisibleTeclado}" Grid.Row="2" HorizontalAlignment="Left" VerticalAlignment="Center" FontWeight="Bold" FontSize="30" Width="AUTO" Content="Dirección" Margin="0,8,0,9"/>
In the second proyect I am writing the property to use in the DataContext of this way:
public String EstadoVisibleTeclado
{
get { return _estadoVisibleTeclado; }
set
{
_estadoVisibleTeclado = value;
this.OnPropertyChanged("EstadoVisibleTeclado");
}
}
and in the constructor of this way:
public Teclado()
{
InitializeComponent();
EstadoVisibleTeclado = "Collapsed";
this.DataContext = this;
}
Someone knows ¿how to shared the binding between proyects?.
someone like this:
Visibility="{PROYECT2.NAMEXAML.Binding EstadoVisibleTeclado}"
Regards.
Estadivisibleeclado should not be a string.

How to bind data to ViewModel wrapper and pass it to RelayCommand as parameter?

I have been searching the web, but can't find the answer i need.
I have a list where the user can choose some items. In my ViewModel it looks like this:
public ObservableCollection<RentalItemVM> ChosenRentalItems {
get { return chosenRentalItems; }
set {
chosenRentalItems = value;
RaisePropertyChanged("ChosenRentalItems");
}
}
When the user is done selecting, he presses a button to save his collection. It works fine if I setup my RelayCommand like this:
public RelayCommand<ObservableCollection<RentalItemVM>> FinishCommand { get; set; }
private void Finish(ObservableCollection<RentalItemVM> chosenItems) {
...
}
Now I need to get the UserId as well, so after some research I found out that I can't pass more than one paramater to the RelayCommand so I need a wrapper. I've made a new ViewModel:
public class OrderVM {
public int UserId { get; set; }
ObservableCollection<RentalItemVM> ChosenItems { get; set; }
}
and changed the RelayCommand to this:
public RelayCommand<OrderVM> FinishCommand { get; set; }
private void Finish(OrderVM order) {
...
}
And i changed the XAML UserControl from this:
<StackPanel Orientation="Horizontal"
Grid.Row="3"
Grid.ColumnSpan="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBox Text="UserId" />
<Button Content="Finish"
Command="{Binding FinishCommand}"
CommandParameter="{Binding ChosenRentalItems}"
UseLayoutRounding="False"
FontSize="24" />
</StackPanel>
to this:
<StackPanel Orientation="Horizontal"
Grid.Row="3"
Grid.ColumnSpan="2"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBox Text="UserId"
x:Name="UserId" />
<Button Content="Finish"
Command="{Binding FinishCommand}"
CommandParameter="{Binding OrderVM}"
UseLayoutRounding="False"
FontSize="24" />
</StackPanel>
Now this is where I get confused. I have no idea how to bind the UserId and ChosenItems. The OrderVM parameter in the RelayCommand is null, so maybe I need to instantiate it first?
Most answers that I found on the web were about displaying data from a ViewModel in a View, but almost no answers on how to post data (or I searched for the wrong things?)
If the command FinishCommand is in the same view model of the DataConext, you can bind UserId too.
<TextBox Text="{Binding UserId} " x:Name="UserId"></TextBox>
and take UserId from your ViewModel instance where command is implemented.
private void Finish(ObservableCollection<RentalItemVM> chosenItems) {
var userid = this.UserId;
foo(userid);
}

Best approach to add an item to a databound Listbox using MVVM pattern?

I'm facing a problem in my WPF project at the moment. At this moment I have a Viewmodel which has a Manager (to communicate with the repo).
internal class TicketViewModel
{
private TicketManager mgr;
public IEnumerable<Ticket> List { get; set; }
public TicketViewModel()
{
mgr = new TicketManager();
List = mgr.GetTickets();
}
}
I've managed to bind this list to the Listbox in my MainWindow. The next step is that I need to add an extra ticket to the list and also pass this through the manager. The problem is I need two parameters from some Controls in the MainWindow. From MVVM perspective I need to use bound Commands on e.g. a Button to communicate with the viewmodel as my viewmodel can't/may not access controls from the window. Is using parameterized Commands the way to go here?
The next problem is that the Listbox won't update I guess. This is the code:
<ListBox x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I found that using a CompareableCollection is the way to go here, but then I still have to read all the Tickets again after adding a new Ticket.
Thanks in advance,
Hicy
okay here is the code.
Lets say you have three textboxes on MainWindow(since you have three Textblocks.) so Your MainWindow.xaml looks like
<Window.DataContext>
<local:MyViewModel/>--set's your viewModel
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="250*"/>
<RowDefinition Height="90"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listboxTix" BorderThickness="0" ItemsSource="{Binding List}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Bisque" Background="Beige" BorderThickness="2">
<StackPanel Width="250">
<TextBlock Text="{Binding TicketNumber}" />
<TextBlock Text="{Binding Text}" />
<TextBlock Text="{Binding State}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox x:Name="TicketNumber" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=Text}" VerticalAlignment="Top" Width="120"/>
<TextBox x:Name="Text" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=State}" />
<TextBox x:Name="State" Grid.Row="1" TextWrapping="Wrap" Text="{Binding Path=TicketNumber}" />
<Button Content="Button" Command="{Binding Path=MainCommand}" Grid.Row="2"/>
</Grid>
and I am assuming that you have some class called class Ticket which contain these three members
Class Ticket
{
public int TicketNumber { get; set; }
public string Text { get; set; }
public string State { get; set; }
}
Now in class TicketManager we fill it with some dummy data
class TicketManager
{
ObservableCollection<Ticket> tl = new ObservableCollection<Ticket>();
internal ObservableCollection<Ticket> GetTickets()
{
tl.Add(new Ticket() { State = "State1", Text = "Text1", TicketNumber = 1 });
tl.Add(new Ticket() { State = "State2", Text = "Text2", TicketNumber = 2 });
tl.Add(new Ticket() { State = "State3", Text = "Text3", TicketNumber = 3 });
return tl;
}
}
and in your Mainwindow ViewModel lets call it MyViewModel.cs we add
class MyViewModel:INotifyPropertyChanged
{
private TicketManager mgr;
public ObservableCollection<Ticket> List { get; set; }
private string text;
private string state;
private int ticketNumber;
private readonly DelegateCommand<object> MyButtonCommand;
public Class1()
{
mgr = new TicketManager();
List = mgr.GetTickets();
MyButtonCommand = new DelegateCommand<object>((s) => { AddListToGrid(text, state, ticketNumber); }, (s) => { return !string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(state); });
}
private void AddListToGrid(string text, string state, int ticketNumber)
{
List.Add(new Ticket() {Text=text,State=state,TicketNumber=ticketNumber });
}
public DelegateCommand<object> MainCommand
{
get
{
return MyButtonCommand;
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public string State
{
get
{
return state;
}
set
{
state = value;
OnPropertyChanged("State");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
public int TicketNumber
{
get
{
return ticketNumber;
}
set
{
ticketNumber = value;
OnPropertyChanged("TicketNumber");
MyButtonCommand.RaiseCanExecuteChanged();
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
You can Modify the code in anyway you want
This ViewModel implements fewthings which are very important from MVVM point of view
1) INotifyPropertyChanged
2) WPF Delegate Command
P.S:The code is tested and it runs as expected
Don't get hung up on MVVM it is simply a separation of data from a view, and models are shared between the two with a majority of the business logic (on a shared component) should be performed on the VM; it is not a religion just a three tiered data system. IMHO
If your button needs to do an operation, have it make a call, most likely in the code behind, to a method on the VM which handles the business logic, updates the list with the new item and notifies the manager.
I would bind the list in question to an ObservableCollection which can notify upon insert/delete of an item.

windows store app(passing two fields through a command parameter) on the click of a button

I am working on a windows store app. I want to be able to pass two parameter on the click of a button to another page.
I am able to pass one conveniently like the line below
CommandParameter="{Binding CustomerServiceRepresentativeId}"
How can I pass an additional field like the one below making two fields passed to the destination page.
Binding CustomerServiceRepresentativeName
I have had no luck passing the two.
<GridView x:Name="GVCSRList" Grid.Column="2" Grid.Row="2" ItemsSource="{Binding}" SelectionMode="None" ItemContainerStyle="{StaticResource GridViewItemStyle1}" Width="Auto" Margin="40,0,0,0">
<GridView.ItemTemplate>
<DataTemplate >
<Button Style="{StaticResource CSRProfile}" Margin="0,0,10,0" Click="ButtonBase_OnClick" CommandParameter="{Binding CustomerServiceRepresentativeId}">
<StackPanel Width="290" Height="45" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0">
<TextBlock Tag="CSRListName" Text="{Binding CustomerServiceRepresentativeName}" Style="{StaticResource CntMedTextBlockStyleBold}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="10,15"/>
</StackPanel>
</Button>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
try
{
var commandParameter = ((Button)sender).CommandParameter;
if (commandParameter != null)
this.Frame.Navigate(typeof(CSRProfile), commandParameter.ToString());
}
catch (Exception ex)
{
throw ex;
}
}
Bind to a new property that has both memebers:
E.g., right now you have:
class MyClass
{
public int CustomerServiceRepresentativeId { get; set; }
public string CustomerServiceRepresentativeName { get; set; }
}
Put those in a separate class, and add a property of that type to your class. E.g.:
class IdNameCombo
{
public int CustomerServiceRepresentativeId { get; set; }
public string CustomerServiceRepresentativeName { get; set; }
}
class MyClass
{
public IdNameCombo IdName { get; set; }
}
Then, you bind to the new property:
CommandParameter="{Binding IdName}"
To set or get the id or the name, you will need to type a little bit more:
MyClass myClass;
myClass.IdName.CustomerServiceRepresentativeId = 1;
myClass.IdName.CustomerServiceRepresentativeName = "Bob";

Categories

Resources