I am new to the MVVM philosophy, and my question may be stupid but hey, I try anyway.
I have a view associated with a model view. In this view, I open a dialog box when the user presses a button.
From this dialog box, I want to refresh the main view without closing the dialog.
The dialog and the main view share the same view model, and I have a property that is binded in the main view.
But when I change the binded property from from the dialog, it is well updated, but not the main view.
Here some code:
MainView.xaml:
<TextBox x:Name="tnNomModele"
HorizontalAlignment="Left"
Height="23"
Margin="142,35,0,0"
TextWrapping="Wrap"
VerticalAlignment="Top"
Width="143"
Text="{Binding MyProperty, UpdateSourceTrigger=PropertyChanged}"/>
ViewModel.cs
public string MyProperty
{
get
{
return _mystring ;
}
set
{
_mystring = value;
RaisePropertyChanged("MyProperty");
}
}
Here's how I call the dialog, on the command binded with a button on the main view:
MyDialog diag = new MyDialog ();
diag.ShowDialog();
And in the command binded with a button on the dialog, I change the value of MyProperty.
But nothing happens in the main view...
I think you need to change your binding:
<TextBox x:Name="tnNomModele" HorizontalAlignment="Left" Height="23" Margin="142,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="143" Text="{Binding MyProperty,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The change is that you're using the default Binding mode, which is OneWay. This means that the ViewModel can push changes to the View but will not receieve changes made from the View. Using TwoWay means that the ViewModel receives notifications from the View when a property changes.
Related
There are 2 pages in my UWP project.
In the following page, I announce a WebView object.
<Page
x:Class="UniversalMarkdownTestApp.WebViewPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:UniversalMarkdownTestApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<WebView Name="WebContent" Grid.Row="0" x:FieldModifier="public"/>
</Grid>
</Page>
Now I want to access this object in other page. How can I achieve this?
You could access this object in other page by the following code:
MainPage mainpage = new MainPage();
var webv = mainpage.WebContent;
But as AbsoluteSith said, this object's instance has been created, if you want to add it to the new page, you could not add directly.
You would need to use this object's property value to create a new object. Please see the following code:
MainPage mainpage = new MainPage();
var webv = mainpage.WebContent;
WebView wv = new WebView();
wv.Source = webv.Source;
grid.Children.Add(wv);
There's another other way to get object in other pages. You could pass object as parameter when navigating. See the following code:
rootFrame.Navigate(typeof(BlankPage),WebContent);
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
WebView wv = new WebView();
wv.Source = ((WebView)e.Parameter).Source;
gd.Children.Add(wv);
}
Here, I still not sure why you want to do this. In general, I don't suggest get controls between the pages. Maybe, if you could tell us your requirement, or What kind of effect you want to achieve, there might be some better solutions.
I'm writing a UserControl, which I intend to use on several pages. It should encapsulate the behaviour to be the same for all pages. But the content and the layout should be different.
On the UserControl I have a ListView whoes ItemSource is bound to a CollectionViewSource with grouping enabled.
<ListView
ItemSource="{Binding Source={StaticResource Collection}}"
ItemTemplate="{Binding GroupedDataTemplate}">
<ListView.GroupStyle>
<GroupSytele HeaderTemplate="{Binding HeaderDataTemplate}"/>
</ListView.GroupStyle>
</ListView>
The UserControl has the DependencyProperties "GroupedDataTemplate", "HeaderDataTemplate" for the layout and one "GroupedCollection" for the data.
On the page, where the UserControl is used, I defined the DataTemplates like:
<controls:MyUserControl
GroupedCollection="{Binding DataContext.MyDataCollection, ElementName=thePage}">
<controls:MyUserControl.GroupedDataTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
</controls:MyUserControl.GroupedDataTemplate>
<controls:MyUserControl.HeaderDataTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</controls:MyUserControl.HeaderDataTemplate>
</controls:MyUserControl>
My problem is, that the DataTemplate definition for "GroupedDataTemplate" works as expected, the description is shown. But for the "HeaderDataTemplate" it doesn't, it is shown only the ToString()-representation of the object.
The setter of the "HeaderDataTemplate" is called and the DataTemplate is assigned to the DependencyProperty of the UserControl.
If I replace the UserControl with the ListView itself, it works as expceted. Thus the binding works propperly to the Description and the Key, but it will only work for description if it is inside the UserControl.
For test purposes I have added a converter to the binding of the Key in the page and it is never called. I all cases, where I define a DataTemplate for an ItemTemplate (ListView or GridView) it works, but is doesn't for the HeaderTemplate of the GroupStyle.
What is my fault?
Very good question, it seems when you use Binding for the HeaderTemplate of GroupSytele, in the .g.cs file of your UserControl, it doesn't generate the update code for HeaderDataTemplate, it means when you define this HeaderDataTemplate property for example like this:
public static readonly DependencyProperty HeaderDataTemplateProperty = DependencyProperty.Register("HeaderDataTemplate", typeof(DataTemplate), typeof(UserGroupedListView), new PropertyMetadata(null));
public DataTemplate HeaderDataTemplate
{
get { return (DataTemplate)GetValue(HeaderDataTemplateProperty); }
set { SetValue(HeaderDataTemplateProperty, value); }
}
The get never gets called.
A workaround here is that you can change the Binding to x:Bind like this in your UserControl:
<ListView ItemsSource="{Binding Source={StaticResource Collection}}" ItemTemplate="{Binding GroupedDataTemplate}">
<ListView.GroupStyle>
<GroupStyle HeaderTemplate="{x:Bind HeaderDataTemplate}" />
</ListView.GroupStyle>
</ListView>
Basically you've done nothing wrong, but it seems data binding for the HeaderTemplate of GroupStyle can only work when it uses x:Bind.
I'm quite new to XAML trying to make grid with Toggle buttons
Something like this:
<GridView ItemSource="{Binding ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Content="{Binding Label}" IsEnabled="{Binding BEnabled}" IsChecked="{Binding BChecked, Mode=TwoWay}"/>
<DataTemplate>
</GridView.ItemTemplate>
</GridView>
I have
ObservableCollection<ButtonClass> ButtonCollection
also class for buttons
class ButtonClass
{
public string Label {get; set;}
public bool BEnabled {get; set:}
public bool BChecked {get;set;}
}
binding works when page loads buttons are displayed from ObservableCollection
But I want collection to update when button IsChecked value changes
also is there any way to bind function to click method like:
Click="{Binding DoWhenClicked}"
Now it just results in error I think that is because DoWhenClicked isn't in ItemSource.
Summary:
I want to have Grid of toggle buttons that binds to some sort of list/array/collection of data with label, checked status, enabled status.
When toggle button is checked I want it to reflect in my collection.
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
What is good way to do this.
I noticed that you asked several question about Template 10 before, so I supposed that you also used Template 10 here for MVVM pattern.
binding works when page loads buttons are displayed from ObservableCollection But I want collection to update when button IsChecked value changes
In Template 10 project, if you want to get notified when parameter in the data model (here means your ButtonClass), you can derive your class from BindableBase. If you check the BindableBase class you will find that it has done the work of INotifyPropertyChanged, it will be much easier here deriving from BindableBase directly rather than implementing INotifyPropertyChanged by yourself.
also is there any way to bind function to click method
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
Instead of Click event, I personally recommend you to using Command in MVVM pattern, and you may want to know which Button is clicked, so you can use CommandParameter here. I created a blank template 10 project and here is my sample:
<GridView x:Name="gridView" RelativePanel.Below="pageHeader" Margin="16,12,0,0" ItemsSource="{x:Bind ViewModel.ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Width="100" Content="{Binding Label}" IsEnabled="{Binding BEnabled}"
IsChecked="{Binding BChecked, Mode=TwoWay}" Command="{Binding ToggleBtnClickCommand}"
CommandParameter="{Binding Label}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The ButtonClass is like this:
public class ButtonClass : BindableBase
{
public string Label { get; set; }
public bool BEnabled { get; set; }
private bool _bChecked;
public bool BChecked
{
get { return _bChecked; }
set
{
_bChecked = value;
RaisePropertyChanged();
}
}
public ICommand ToggleBtnClickCommand { get; set; }
}
And the MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
public ObservableCollection<ButtonClass> ButtonCollection;
public MainPageViewModel()
{
ButtonCollection = new ObservableCollection<ButtonClass>();
for (int i = 0; i < 20; i++)
{
ButtonCollection.Add(new ButtonClass
{
Label = "TButton " + i,
BChecked = true,
BEnabled = true,
ToggleBtnClickCommand = new DelegateCommand<string>(ToggleBtnClicked)
});
}
}
public void ToggleBtnClicked(string obj)
{
var buttonLable = obj;
}
}
In case you need to check my sample, you can find my demo here.
There are a few concepts that are missing from your code that you need to properly implement for this to work
INotifyPropertyChanged -> Your ButtonClass class acts as a view model for your view. As such, for values to properly update UI and the other way around you need your class to implement it and all your properties to raise the PropertyChanged event when their values change (in the setter)
2-Way bindings -> If you want your ButtonClass instance to update when you click the button and you want your button to change state if you change the property in the ButtonClass, your bindings need to be Mode=TwoWay. You might want to explore the new Bindings {x:Bind} for better performance
Commands -> To bind the click event, you need to use the Command property. You would need to create a ICommand implementation, and create a property in your ButtonClass. The use Command property of the Button to bind to that Command.
Essentially, everything I mentioned are components of an MVVM framework, of which there are many out there. Here are some of the more popular ones: Caliburn, MVVMCross, Prism.
There's also a great starter kit for Windows 10 that will definitely kick start your app and contains a lot of this classes already built - Template10
I'm having a problem hiding one pane and showing another in my Apache Pivot app. In my BXML file I have two BoxPanes in a window. Pane 1 starts visible and pane 2 starts hidden:
<BoxPane bxml:id="pane1" orientation="vertical" styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
<Label text="Pane 1"/>
<PushButton bxml:id="startButton" buttonData="Start"/>
</BoxPane>
<BoxPane bxml:id="pane2" orientation="vertical" visible="false" styles="{horizontalAlignment:'center', verticalAlignment:'center'}">
<Label text="Pane 2"/>
</BoxPane>
And I have a listener added to the button that should make pane 1 hidden and pane 2 visible:
#BXML private PushButton startButton = null;
#BXML private BoxPane pane1 = null;
#BXML private BoxPane pane2 = null;
#Override
public void initialize(Map<String, Object> namespace, URL location, Resources resources)
{
startButton.getButtonPressListeners().add(new ButtonPressListener()
{
#Override
public void buttonPressed(Button button)
{
start();
}
});
}
private void start()
{
pane1.setVisible(false);
pane2.setVisible(true);
}
When I click the button though, pane 1 is hidden and pane 2 never shows up. Same thing happens when I reverse the order of the statements in start().
Interestingly enough, when I comment out pane1.setVisible(false), then pane 2 does show up when I click the button.
This is my first Pivot app, so maybe there's some fancy container that does what I want to do in a better way, but I'd still like to know what is going on here. What I'm trying to do seems pretty simple, and I'm sort of baffled why it doesn't work.
You might want to try using a CardPane to switch between your two views. The tutorial on that is here: http://pivot.apache.org/tutorials/card-panes.html
The basic idea is to have the CardPane "host" your two BoxPanes, something like this:
<CardPane bxml:id="cardPane">
<BoxPane bxml:id="pane1" ...>
<Label text="Pane 1"/>
...
</BoxPane>
<BoxPane bxml:id="pane2" ...>
<Label text="Pane 2"/>
</BoxPane>
</CardPane>
Make both BoxPanes visible. Then when you want to change between them, use cardPane.setSelectedIndex(...);
I've created a custom control with a dependency property for databinding.
The binded value should then be displayed in a text box.
This binding works properly.
The problem occurs when I implement my custom control. The grid's data context is a simple view model which contains a String property for binding.
If I bind this property to a standard wpf controls text box everything works fine.
If I bind the property to my custom control nothing happens.
After some debugging I found out that SampleText is searched in CustomControl. Of course it doesn't exist there.
Why is my property searched in CustomControl and not taken from the DataContext as it happens in scenario 1.
<Window x:Class="SampleApplicatoin.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:SampleApplication"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.DataContext>
<controls:ViewModel/>
</Grid.DataContext>
<TextBox Text="{Binding SampleText}"/>
<controls:CustomControl TextBoxText="{Binding SampleText}"/>
</Grid>
</Window>
Below the XAML code of the custom control.
I use DataContext = Self to get the dependency property from code behind:
<UserControl x:Class="SampleApplication.CustomControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" Margin="87,133,0,0" TextWrapping="Wrap" Text="{Binding TextBoxText}" VerticalAlignment="Top" Width="120"/>
</Grid>
</UserControl>
The xaml.cs file just contains the dependency property:
public partial class CustomControl : UserControl
{
public static readonly DependencyProperty TextBoxTextProperty = DependencyProperty.Register("TextBoxText", typeof (String), typeof (CustomControl), new PropertyMetadata(default(String)));
public CustomControl()
{
InitializeComponent();
}
public String TextBoxText
{
get { return (String) GetValue(TextBoxTextProperty); }
set { SetValue(TextBoxTextProperty, value); }
}
}
Thanks for any help on this. It really drives me crazy now.
EDIT:
I just came over two possible solutions:
Here the first (which works for me):
<!-- Give that child a name ... -->
<controls:ViewModel x:Name="viewModel"/>
<!-- ... and set it as ElementName -->
<controls:CustomControl TextBoxText="{Binding SampleText, ElementName=viewModel}"/>
The second one. This doesn't work in my case. I don't know why:
<controls:CustomControl TextBoxText="{Binding SampleText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type controls:ViewModel}}}"/>
<!-- or -->
<controls:CustomControl TextBoxText="{Binding SampleText, RelativeSource={RelativeSource FindAncestor, AncestorType=controls:ViewModel}}"/>
I had a similar situation.
In my case, I fixed it with adding OnPropertyChanged in setter of property in ViewModel.