C# [ASP .Net MVC] Daten des ViewModels werden nicht richtig an Controller gesendet.

Zephyro

Ensign
Registriert
Juni 2011
Beiträge
138
Hallo,

ich habe Schwierigkeiten bei dem auslesen eines ViewModels in meinem Controller.
In dem zugehörigen view befinden sich mehrere Form-Tags. Ich kann aber nur die Daten des ersten Form-Blocks empfangen. Wenn ich beispielsweise die Daten des zweiten Form-Blocks über mein ViewModel empfangen möchte, dann bekomme ich eine ArgumentNullException.

Beispiel Code:

viewModel:

Code:
public class MeinViewModel
{
	public ObservableCollection<typ1> daten1
        {
            get;
            set;
        }

	public ObservableCollection<typ2> daten2
        {
            get;
            set;
        }
}

View:
Code:
@using (Html.BeginForm("ControllerFunktion1", "Controller", FormMethod.Post, new { @id="form1" }))
{
	// html elemente... für daten1
	<input type="button" class="button" id="button1" value="button1" /><br />
<script type="text/javascript">
	$('#button1').click(function () {
                $('#form1').attr('action', '@Url.RouteUrl("Default", new { controller = "Controller", action = "ControllerFunktion1" })');
                $('#form1').trigger('submit');
        });
</script>
 Html.EndForm();
}

@using (Html.BeginForm("ControllerFunktion1", "Controller", FormMethod.Post, new { @id="form2" }))
{
	// html elemente... für daten2
	<input type="button" class="button" id="button2" value="button2" /><br />
<script type="text/javascript">
	$('#button2').click(function () {
                $('#form2').attr('action', '@Url.RouteUrl("Default", new { controller = "Controller", action = "ControllerFunktion2" })');
                $('#form2').trigger('submit');
        });
</script>
 Html.EndForm();
}

Controller:

Code:
public ActionResult ControllerFunktion1(MeinViewModel viewModel)
{
	// code...
	return RedirectToAction(...);
}
// Hier sind im viewModel daten2 = null, was stimmt. daten1 sind gefüllt!

public ActionResult ControllerFunktion2(MeinViewModel viewModel)
{

	// code...
	return RedirectToAction(...);
}
// Hier sind im viewModel daten1 & daten2 = null. Normalerweise sollten jetzt hier die daten1 = null, und die daten2 gefüllt sein.

Ich kann nur in der ControllerFunktion1 auf die Daten des viewModels zugreifen. In Controllerfunktion2 komischerweise nicht, obwohl ich alles gleich handhabe. Habe schon den Code der Seite mit Firebug angeschaut und die einzelnen Form-Blöcke stimmen bzw. haben an der richtigen Stelle ihr schließendes form-Tag.

Hat jemand eine Idee, was hier falsch sein könnte?

Edit:
Hier die Fehlermeldung die auftritt, wenn ich auf die viewModel-Daten in der ControllerFunktion2 zugreifen möchte:
Code:
Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: source

MfG Zephyro
 
Zuletzt bearbeitet:
Zephyro schrieb:
Beispiel Code:

viewModel:

Code:
public class MeinViewModel
{
	public ObservableCollection<typ1> daten1
        {
            get;
            set;
        }

	public ObservableCollection<typ2> daten2
        {
            get;
            set;
        }
}

MVC und ViewModel passt nicht so richtig zusammen ;) Sag einfach Model. Die ObservableCollection kannst du dir auch sparen, bringt dir ohne die entsprechende Desktop-App gar nichts.


Zephyro schrieb:
View:
Code:
@using (Html.BeginForm("ControllerFunktion1", "Controller", FormMethod.Post, new { @id="form1" }))
{
	// html elemente... für daten1
	<input type="button" class="button" id="button1" value="button1" /><br />
<script type="text/javascript">
	$('#button1').click(function () {
                $('#form1').attr('action', '@Url.RouteUrl("Default", new { controller = "Controller", action = "ControllerFunktion1" })');
                $('#form1').trigger('submit');
        });
</script>
 Html.EndForm();
}

@using (Html.BeginForm("ControllerFunktion1", "Controller", FormMethod.Post, new { @id="form2" }))
{
	// html elemente... für daten2
	<input type="button" class="button" id="button2" value="button2" /><br />
<script type="text/javascript">
	$('#button2').click(function () {
                $('#form2').attr('action', '@Url.RouteUrl("Default", new { controller = "Controller", action = "ControllerFunktion2" })');
                $('#form2').trigger('submit');
        });
</script>
 Html.EndForm();
}

Was ist denn das? :D Doppelt-gemoppelt hält besser? ;)

Code:
@model Bla.Blub

@using (Html.BeginForm("Action1", "Controller"))
{
    // hier irgendwas mit dem Model machen
    @Html.TextBoxFor( m => m.ModelProperty )
    <button>Submit</button>
}

@using (Html.BeginForm("Action2", "Controller"))
{
    // hier wieder irgendwas mit dem Model machen
    @Html.TextBoxFor( m => m.AnotherModelProperty )
    <button>Submit</button>
}

Edit.
Dein Controller muss natürlich auch das entsprechende Model übergeben.
Bsp. für den Fall, dass das der Index-View ist:

Code:
public ActionResult Index()
{
    // nur als Beispiel.
    Model model = _datasource.Models.First( m => m.ID == 1 );
    return View( model );
}
 
Zuletzt bearbeitet:
holy schrieb:
Was ist denn das? :D Doppelt-gemoppelt hält besser? ;)
Ja :D... Die Html.EndForm() hab ich nur mal zum testen rein gemacht.
Edit.
Dein Controller muss natürlich auch das entsprechende Model übergeben.
Bsp. für den Fall, dass das der Index-View ist:

Code:
public ActionResult Index()
{
    // nur als Beispiel.
    Model model = _datasource.Models.First( m => m.ID == 1 );
    return View( model );
}

Das ist mir klar! Ich sehe in meinem View auch die richtigen Daten, die ich im Controller an den View übergeben habe.

Code:
    @model Bla.Blub
     
    @using (Html.BeginForm("Action1", "Controller"))
    {
    // hier irgendwas mit dem Model machen
    @Html.TextBoxFor( m => m.ModelProperty )
    <button>Submit</button>
    }
     
    @using (Html.BeginForm("Action2", "Controller"))
    {
    // hier wieder irgendwas mit dem Model machen
    @Html.TextBoxFor( m => m.AnotherModelProperty )
    <button>Submit</button>
    }

Habe es mal so ausprobiert, aber es ändert auch nichts an meinem Problem.
 
Dann machst du irgendwas falsch.
Hier ein etwas ausführlicheres Beispiel. Wahrscheinlich ist dein Controller falsch.

Das Model:
Code:
public class MeinModel
{
    public string Foo { get; set; }
    public string Bar { get; set; }
}

Der View:
Code:
@model MVC3_Playground.Models.MeinModel
           
<h1>@ViewBag.Message</h1>

<p>
    @using (Html.BeginForm("UpdateModel1", "Home"))
    {
        @Html.TextBoxFor( m => m.Foo )
        <br />
        <button>Submit</button>
    }
</p>

<p>
    @using (Html.BeginForm("UpdateModel2", "Home"))
    {
        @Html.TextBoxFor( m => m.Bar )
        <br />
        <button>Submit</button>
    }
</p>

Der Controller:
Code:
public class HomeController : Controller
{
    public ActionResult Index()
    {
        MeinModel model = new MeinModel();
        return View( model );
    }

    public ActionResult About()
    {
        return View();
    }

    public ActionResult UpdateModel1()
    {
        MeinModel model = new MeinModel();
        if ( TryUpdateModel( model ) )
        {
            ViewBag.Message = model.Foo;
        }

        return View( "Index" );
    }

    public ActionResult UpdateModel2()
    {
        MeinModel model = new MeinModel();
        if ( TryUpdateModel( model ) )
        {
            ViewBag.Message = model.Bar;
        }

        return View( "Index" );
    }
}

Probier es aus. Funktioniert einwandfrei ;)


Edit.
Hier noch die Screenshots, falls du mir nicht glaubst ;)
(Benutze das Standard-MVC3 Template)
 

Anhänge

  • UpdateModel1.PNG
    UpdateModel1.PNG
    2,8 KB · Aufrufe: 180
  • UpdateModel1_Result.PNG
    UpdateModel1_Result.PNG
    3,5 KB · Aufrufe: 186
  • UpdateModel2.PNG
    UpdateModel2.PNG
    3,9 KB · Aufrufe: 200
  • UpdateModel2_Result.PNG
    UpdateModel2_Result.PNG
    3,6 KB · Aufrufe: 183
Zuletzt bearbeitet:
Vielen Dank für deine Mühen!
Ich glaube dir übrigends ;)

Bis jetzt habe ich den Fehler leider noch nicht gefunden, aber evtl. sollte ichs mir morgen nochmal in ruhe anschauen.
Manchmal sieht man ja den Wald vor lauter Bäumen nicht mehr :)

Ich melde mich wieder hier...
 
Es ist kein Fehler. Im Beispiel von holy werden einfache Properties verwendet, im Beispiel des TE Collections. Hier spielt das ModelBinding eine Rolle.

Eine Möglichkeit ist, für die Properties, die bei einem Post zurück geschickt werden sollen, ein Hiddenfield anzulegen.

Controller mit 2 Methoden für GET und POST

Code:
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var personViewModel = new PersonViewModel { People = GetPeople() };

            return View(personViewModel);
        }

        [HttpPost]
        [ActionName("Index")]
        public ActionResult IndexPost(PersonViewModel model)
        {

            var indexViewModel = new PersonViewModel() { People = new List<PersonModel>() };
            UpdateModel(indexViewModel);

            return View();
        }
        
        private IList<PersonModel> GetPeople()
        {
            var personList = new List<PersonModel>();

            for (int i = 0; i < 20; i++)
            {
                personList.Add(new PersonModel() { Id = i + 1, Name = "Hans Wurst " + i });
            }

            return personList;
        } 
    }

Ein Teil des Views
Code:
@using (Html.BeginForm())
{
    <ul>
        @foreach (var person in Model.People)
        {
            <li>
                <input type="hidden" value="@Model.People[index].Id" name="@Html.NameFor(x => x.People[index].Id)"/>
                <input type="hidden" value="@Model.People[index].Name" name="@Html.NameFor(x => x.People[index].Name)"/>
                @Html.Raw(person.Name)
            </li>

            index++;
        }
    </ul>
    
    <input type="submit" value="Absenden" />
}

Nach dem UpdateModel() hast Du in der Collection wieder alle Werte drin stehen, die auch vorher an den View gesendet wurden.

Das MVC und ViewModels nicht zusammenpassen, halte ich allerdings für ein Gerücht.

EDIT: Das komplette DemoProjekt (VS2012 + .NET4 + MVC4)
http://sdrv.ms/RF8DeX
 
Zuletzt bearbeitet: (Link hinzugefügt)
lbm1305 schrieb:
Das MVC und ViewModels nicht zusammenpassen, halte ich allerdings für ein Gerücht.

Das passiert wenn ein XAMLianer über MVC redet, sorry. Hätte ich den Namen bestimmen können, würden ViewModels in MVC MultiModel heißen, aber nicht ViewModel :)
 
Moin,

@lbm1305

Danke für den Tipp mit dem ModelBinding... genau das wars.
In dem View, wo ich die Daten aus dem ViewModel auslese, hatte ich zuerst die Textfelder wie folgt erstellt:
Code:
@Html.TextBoxFor(model => model.MyCollection.ElementAt(i).Property)
und nicht so:
Code:
@Html.TextBoxFor(model => model.MyCollection[i].Property)

Keine Ahnung wieso ich da nicht selbst drauf gekommen bin :).
Vielen Dank für die Hilfe!

Viele Grüße,
Zephyro
 
Zurück
Oben