0

I'm very new to WPF and currently learning the concepts of data binding.

my simplified XAML code. besides my problem (below) it works fine - quick and dirty placing of objects via GUI, will be cleaned up once works:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition/>
            <RowDefinition Height="40"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
        </Grid>
        <Grid Grid.Row="1">
            <GroupBox Header="Change Type:" Height="95" Width="100" VerticalAlignment="Top" Margin="270,4,422,0" >
                <StackPanel>
                    <RadioButton x:Name="RbtAdd" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Top" GroupName="modGroup" Checked="ModeRadio_Checked">
                        <WrapPanel>
                            <TextBlock Text="Add" Foreground="Green"/>
                        </WrapPanel>
                    </RadioButton>
                    <RadioButton x:Name="RbtPull" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Top" GroupName="modGroup" Checked="ModeRadio_Checked">
                        <WrapPanel>
                            <TextBlock Text="Pull" Foreground="Blue"/>
                        </WrapPanel>
                    </RadioButton>
                    <RadioButton x:Name="RbtModify" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Top" GroupName="modGroup" Checked="ModeRadio_Checked">
                        <WrapPanel>
                            <TextBlock Text="Modify" Foreground="DarkGray"/>
                        </WrapPanel>
                    </RadioButton>
                </StackPanel>
            </GroupBox>

            <TextBlock x:Name="txtCurStock" HorizontalAlignment="Left" Margin="330,181,0,0" TextWrapping="Wrap" Text="{Binding Path=CurrentStock}" VerticalAlignment="Top" FontSize="20" FontWeight="Bold" TextAlignment="Center"/>

            <Label Content="Current stock:" HorizontalAlignment="Left" Margin="289,156,0,0" VerticalAlignment="Top"/>
            <Label x:Name ="lblOperation" Content="Stock to Pull:" HorizontalAlignment="Left" Margin="507,156,0,0" VerticalAlignment="Top"/>
            <TextBox x:Name="txtEntry" HorizontalAlignment="Left" Height="32" Margin="489,181,0,0" TextWrapping="Wrap" TextAlignment="Center" Text="{Binding Path=ModEntry}" VerticalAlignment="Top" Width="120" FontSize="20" FontWeight="Bold" TextChanged="TxtEntry_TextChanged"/>
            <Label Content="New Stock" HorizontalAlignment="Right" Margin="714,156,0,0" VerticalAlignment="Top" Width="68"/>
            <TextBlock Text="{Binding Path=NewStock}" HorizontalAlignment="Right" Margin="0,186,10,0" TextAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="20" FontWeight="Bold" Width="68"/>
            <TextBox x:Name="txtComment"  HorizontalAlignment="Left" Height="86" Margin="289,233,0,0" TextWrapping="Wrap" Text="{Binding Path=ModEntry}" VerticalAlignment="Top" Width="493"/>
            <Label Content="Comment:" HorizontalAlignment="Left" Margin="289,207,0,0" VerticalAlignment="Top"/>

            <TextBlock x:Name ="txtModindicator" HorizontalAlignment="Left" Margin="433,181,0,0" TextWrapping="Wrap" Text="-" FontSize="20" FontWeight="Bold" VerticalAlignment="Top"/>
            <TextBlock x:Name ="txtResindicator" HorizontalAlignment="Left" Margin="663,182,0,0" TextWrapping="Wrap" Text="=" FontSize="20" FontWeight="Bold" VerticalAlignment="Top"/>

        </Grid>
    </Grid>

now the shortened c# code:

using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Runtime.CompilerServices;


namespace SomeWPF
{
    /// <summary>
    /// Interaction logic for ModifyWindow.xaml
    /// </summary>
    public partial class MainWindow : INotifyPropertyChanged
    {

        public enum Mymode
        {
            add,
            pull,
            modify
        }

        public Mymode mode;

        public MainWindow()
        {
            DataContext = this;
            InitializeComponent();

            CurrentStock = 5;
            RbtPull.IsChecked = true;
            ModEntry = 1;

        }

        private void ModeRadio_Checked(object sender, RoutedEventArgs e)
        {
            if (sender != null)
            {
                if (sender.Equals(RbtAdd))
                {
                    mode = Mymode.add;
                    txtModindicator.Text = "+";
                    txtComment.Text = "Add";
                    lblOperation.Content = "Stock to Add:";
                }
                else if (sender.Equals(RbtPull))
                {
                    mode = Mymode.pull;
                    txtModindicator.Text = "-";
                    txtComment.Text = "Pull";
                    lblOperation.Content = "Stock to Pull:";
                }
                else
                {
                    mode = Mymode.modify;
                    txtModindicator.Text = "~";
                    lblOperation.Content = "Corrected Quantity:";
                    txtComment.Text = "Mod";
                }
                TxtEntry_TextChanged(sender, null);
            }
        }

        private void TxtEntry_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (mode == Mymode.add)
            {
                NewStock = CurrentStock + ModEntry;
            }
            else if (mode == Mymode.pull)
            {
                NewStock = CurrentStock - ModEntry;
            }
            else
            {
                NewStock = ModEntry;
            }

        }

        #region Binding Stuff

        private int _newstock;
        public int NewStock
        {

            get
            {

                return _newstock;
            }
            set
            {

                if (_newstock != value)
                {
                    _newstock = value;
                    OnPropertyChanged();
                }
            }
        }

        private int _modentry;
        public int ModEntry
        {
            get
            {
                return _modentry;
            }
            set
            {
                if (_modentry != value)
                {
                    _modentry = value;
                    OnPropertyChanged();
                }
            }
        }

        private int _currentstock;
        public int CurrentStock
        {
            get
            {
                return _currentstock;
            }
            set
            {
                if (_currentstock != value)
                {
                    _currentstock = value;
                    OnPropertyChanged();
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        #endregion


    }
}

So this window is a popup in a little program for an inventory storage for the users to enter movements of the inventory. everything loads fine so far and I now wanted to do the quite simple calculation part. with "old" winforms c# you'd just take the values and update the text property of the result "manually" but of course we (I) want to learn new stuff and do stuff with data binding. The code also does the calculation, but the trigger is somehow not what I want.

let's say current stock is 5 when window loads, the mode is set to Pull (RbtPull) and the user entry (Binding to ModEntry) is set to 1 via code. The NewStock therefore should be 4 which displays correctly. (yey) Also the comment field (for debugging for now) displays the ModEntry value 1. so far so good.

Now I enter 3 in the Stock to Pull field, but nothing happens. (I want it to react "realtime"). The new Stock is still displayed as 4, the comment is still displayed as 1. When I leave the field (and click into the comment field) - the property change is detected and the Comment Field shows also 3 (=ModEntry) - so it's not "realtime" but only triggers when the field is losing focus, but that would be also acceptable.

The real problem is: The new Stock stays 4 and does not calculate. Now when I enter the Stock to Pull field again and change the value to let's say 5, the New Stock field updates to 2 (so to the value I entered before 5-3=2) Overwriting the field with again 5 will change the new Stock to 0. So it's always "one step behind".

From what I have found i have an idea, that I need some kind of Binding Converter instead of my method of calculating things, but I can't really find anything suitable and am not familiar enough yet with data binding. Have tried out some things already directly in the binding variable code but none worked. If anyone could hint me in the right direction I'd be very thankful. (don't need a silver plate solution but just an idea what way to search (e.g. if the sort of binding I use makes sense at all or if there's something I have to add etc.).

Thanks a lot!

PS: of course if someone is motivated to give a silver plate solution I'd also be grateful. :) - and sorry for the bad english, no native speaker.

doomi
  • 53
  • 7
  • PPS: trigger also works fine when changing the radio buttons, it updates the new stock as expected, but changing the stock change not triggering problem remains. – doomi Dec 08 '18 at 21:37
  • 1
    These links may help you https://stackoverflow.com/questions/397556/how-to-bind-radiobuttons-to-an-enum and https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.binding.updatesourcetrigger?view=netframework-4.7.2 – nosale Dec 08 '18 at 22:24
  • 1
    Hi @doomi , the code you provided is not working , if you can post a working code we can help you more , also whey "txtEntry" is binding to "ModEntry" txtEntry need to be a number as i understand and not "Mode". Also numbers need to be int not string , no need to convert them each time to int before and then back to string . i didnt see the issue you are describing because the code is not compiling but please read the following https://stackoverflow.com/a/13601285/7979573 – kojan abzah Dec 09 '18 at 07:49
  • Thanks for your replies! nosale: will look into those links, thx! @kojanabzah: I edited the initial post, it should be compilable now. I also changed the strings to int (didn't know that was so easy for textboxes in WPF) – doomi Dec 10 '18 at 07:54
  • oh and: txtEntry contains the number to modify the stock, hence binding to ModEntry (Mod like (stock) modification, not like mode - could be a bit more consistent, yes) – doomi Dec 10 '18 at 08:05

1 Answers1

0

@nosale 's second link (see comments) provided the answer to the Problem. Setting both XAML fields txtEntry and the Result field to UpdateSourceTrigger=PropertyChanged solved the issue.

so the correct block Looks like this now without changes to the c# code:

    <Label Content="Current stock:" HorizontalAlignment="Left" Margin="289,156,0,0" VerticalAlignment="Top"/>
    <Label x:Name ="lblOperation" Content="Stock to Pull:" HorizontalAlignment="Left" Margin="507,156,0,0" VerticalAlignment="Top"/>
    <TextBox x:Name="txtEntry" HorizontalAlignment="Left" Height="32" Margin="489,181,0,0" TextWrapping="Wrap" TextAlignment="Center" Text="{Binding Path=ModEntry, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120" FontSize="20" FontWeight="Bold" TextChanged="TxtEntry_TextChanged"/>
    <Label Content="New Stock" HorizontalAlignment="Right" Margin="714,156,0,0" VerticalAlignment="Top" Width="68"/>
    <TextBlock Text="{Binding Path=NewStock, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Right" Margin="0,186,10,0" TextAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Top" FontSize="20" FontWeight="Bold" Width="68"/>
    <TextBox x:Name="txtComment"  HorizontalAlignment="Left" Height="86" Margin="289,233,0,0" TextWrapping="Wrap" Text="{Binding Path=ModEntry}" VerticalAlignment="Top" Width="493"/>
    <Label Content="Comment:" HorizontalAlignment="Left" Margin="289,207,0,0" VerticalAlignment="Top"/>

Reason is, that textboxes have a Default UpdateSourceTrigger=LostFocus and not PropertyChanged to prevent updates with user having entered typos.

something new learned: WPF is cool and automatically handles non plausible values like null or strings and marks the field red! :)

thanks again for the links!

doomi
  • 53
  • 7